| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- <template>
- <div class="lab-designer-container">
- <SpreadDesigner class="spread-designer-container" v-loading="loading" businessType="2" :businessId="props.formData.id" ref="spreadDesignerRef"
- @init="handleDesignerInit" style="width: 100%;padding: 10px 20px 10px 10px;height: 650px">
- <!-- @init="handleDesignerInit" style="width: 100%;padding: 10px 20px 10px 10px;height: 750px">-->
- <template #toolbar>
- <el-button type="warning" @click="handleConfig">模版配置</el-button>
- <el-button type="primary" @click="handlePreview">模版预览</el-button>
- <el-button type="primary" @click="handleSave">保存</el-button>
- <el-button type="default" @click="handleCancel">取消</el-button>
- <!-- <el-switch v-model="autoCopy" active-text="复制模式" />-->
- </template>
- </SpreadDesigner>
- <el-dialog v-model="isConfig" width="380px" height="80%" title="模版配置" :modal="false"
- modal-class="config-dialog-modal" !close-on-click-modal draggable overflow>
- <!-- 续页配置 -->
- <el-form>
- <el-form-item label="是否存在续页" required>
- <el-switch v-model="isContinuePage" active-text="是否续页"/>
- </el-form-item>
- <el-form-item label="续页工作表名称" required>
- <el-input v-model="continuePageSheetName" :disabled="!isContinuePage"
- placeholder="请输入续页工作表"/>
- </el-form-item>
- <el-form-item label="隐藏空白续页">
- <el-switch v-model="isHiddenContinuePage" :disabled="!isContinuePage" active-text="隐藏续页"/>
- </el-form-item>
- </el-form>
- <el-card header="续页复制范围" style="margin: 20px;" v-if="isContinuePage">
- <el-alert title="选择需要复制表格的4个角,点击复制坐标" type="info" style="margin-bottom: 10px;"
- :closable="false"/>
- <el-form inline>
- <el-row :gutter="10">
- <el-col :span="12">
- <el-input v-model="copyRangeTopLeft" placeholder="左上角">
- <template #append>
- <el-button type="primary" @click="handleCopy('lt')">复制</el-button>
- </template>
- </el-input>
- </el-col>
- <el-col :span="12">
- <el-input v-model="copyRangeTopRight" placeholder="右上角">
- <template #append>
- <el-button type="primary" @click="handleCopy('rt')">复制</el-button>
- </template>
- </el-input>
- </el-col>
- </el-row>
- <p style="height: 5px;"></p>
- <el-row :gutter="10">
- <el-col :span="12">
- <el-input v-model="copyRangeBottomLeft" placeholder="左下角">
- <template #append>
- <el-button type="primary" @click="handleCopy('lb')">复制</el-button>
- </template>
- </el-input>
- </el-col>
- <el-col :span="12">
- <el-input v-model="copyRangeBottomRight" placeholder="右下角">
- <template #append>
- <el-button type="primary" @click="handleCopy('rb')">复制</el-button>
- </template>
- </el-input>
- </el-col>
- </el-row>
- </el-form>
- </el-card>
- <template #footer>
- <el-button type="primary" @click="saveConfig">保存</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts">
- import '@grapecity-software/spread-sheets-designer-resources-cn'
- import * as GC from '@grapecity-software/spread-sheets'
- import SpreadDesigner from '@/views/pressure2/dynamictb/SpreadDesigner/index.vue'
- import {useTagsViewStore} from '@/store/modules/tagsView'
- import {
- createStandardTemplateV2,
- getStandardTemplateInfo,
- updateStandardTemplate
- } from '@/api/pressure2/standard/template'
- import {useRoute, useRouter} from 'vue-router'
- import {buildFileUrl} from '@/utils'
- import {DynamicTbApi} from '@/api/pressure2/dynamictb'
- import {DynamicTbInsApi} from '@/api/pressure2/dynamictbins'
- import axios from 'axios'
- const route = useRoute()
- const router = useRouter();
- const tagsViewStore = useTagsViewStore()
- defineOptions({ name: 'SpreadEditor' });
- let designer = null
- const spreadDesignerRef = ref(null)
- const loading = ref(false)
- const isConfig = ref(false)
- // 续页配置相关变量
- const isContinuePage = ref(false)
- const continuePageSheetName = ref('续页')
- const isAutoCopy = ref(false)
- const isHiddenContinuePage = ref(false)
- // 复制范围相关变量
- const copyRangeTopLeft = ref('')
- const copyRangeTopRight = ref('')
- const copyRangeBottomLeft = ref('')
- const copyRangeBottomRight = ref('')
- const props = defineProps({
- dtParams: {
- type: Object,
- default: () => ({}),
- required: false
- },
- formData: {
- type: Object,
- default: () => ({}),
- required: false
- }
- });
- const getTbCols = async () => {
- return await DynamicTbApi.getDtCols(props.dtParams);
- }
- const handleDesignerInit = (instance) => {
- designer = instance
- if (props.formData.id) {
- fetchTemplateData()
- }
- }
- // 取消编辑
- const handleCancel = () => {
- tagsViewStore.closeSelectedTag(route)
- }
- // 保存编辑
- const handleSave = () => {
- loading.value = true
- const formData = new FormData()
- const spread = designer.getWorkbook()
- // 工作表数据
- let bindingPathSchema = spreadDesignerRef.value?.getDefaultSchema()
- // 字段列表释义
- let bindingPathNameJson = spreadDesignerRef.value?.handleUpdateDesignerState.get('bindingPathDataJSON')
- bindingPathNameJson = !bindingPathNameJson ? '[]' : JSON.stringify(bindingPathNameJson)
- spread.save(async function (blob) {
- loading.value = false
- let result = null
- formData.append('file', blob)
- formData.append('id', props.formData.id)
- formData.append('name', props.formData.tbName)
- formData.append('signType', '')
- formData.append('classId', props.formData.tbType)
- formData.append('type', '2')
- // formData.append('code', props.formData.tbCode)
- formData.append('versionNumber', props.formData.tbCode)
- formData.append('status', 0)
- formData.append('bindingPathSchema', !bindingPathSchema ? '' : bindingPathSchema)
- formData.append('bindingPathNameJson', bindingPathNameJson)
- if (isNewTemplate) {
- // let params = {
- // file: blob,
- // id: props.formData.id,
- // name: props.formData.tbName,
- // signType: '',
- // classId: props.formData.tbType,
- // type: '2',
- // // code: props.formData.tbCode,
- // versionNumber: props.formData.tbCode,
- // status: 0,
- // bindingPathSchema: !bindingPathSchema ? '' : bindingPathSchema,
- // bindingPathNameJson: bindingPathNameJson,
- // }
- result = await createStandardTemplateV2(formData)
- isNewTemplate = false;
- } else {
- result = await updateStandardTemplate(formData);
- }
- if (result) {
- loading.value = false
- ElMessage.success('保存成功')
- // tagsViewStore.closeSelectedTag(route)
- }
- })
- }
- // 预览
- const handlePreview = () => {
- DynamicTbInsApi.getOrCreatePreviewData(props.formData.id).then(res => {
- router.push({
- path: `/cybggl/preview/${props.formData.id}/${res.refId}`
- });
- })
- }
- let isNewTemplate = false;
- // 获取模版详情
- const fetchTemplateData = async () => {
- loading.value = true
- const templateRes = await getStandardTemplateInfo({ id: props.formData.id })
- const spread = designer.getWorkbook()
- if (templateRes && templateRes.id) {
- // 已有模板的情况
- let bindingPathSchema = JSON.parse(templateRes.bindingPathSchema);
- let properties = {}; //bindingPathSchema.properties;
- let bindingPathName = [];
- let schemaSources = await getTbCols();
- schemaSources?.forEach(element => {
- /*if (element.col_val_type == 5){
- let items = {
- type: 'object',
- properties: {
- },
- }
- element.note.split(',').forEach(element2 => {
- items.properties[element2] = {
- type: 'string',
- }
- });
- properties[element.col_code] = {
- dataFieldType: 'table',
- type: 'array',
- items: items
- }
- }else {*/
- properties[element.colCode] = {
- dataFieldType: 'text',
- type: 'string'
- }
- // }
- bindingPathName.push(
- {
- "dataFieldType": "text",
- "field": element.colCode,
- "displayName": element.colName,
- "type": "string",
- "isVerify": "0"
- }
- )
- });
- bindingPathSchema.properties = properties;
- spreadDesignerRef.value.setDefaultSchema(JSON.stringify(bindingPathSchema), JSON.stringify(bindingPathName))
- if (!templateRes.fileUrl) return loading.value = false
- // 获取模板文件流
- const fileUrl = !templateRes.fileUrl ? '' : buildFileUrl(templateRes.fileUrl)
- const response = await axios.get(fileUrl, { responseType: 'blob' });
- const blob = new Blob([response.data], { type: response.headers['content-type'] });
- // 渲染葡萄城文件
- spread.open(blob, () => {
- spread.getActiveSheet().zoom(1)
- // 设置数据源的值
- spreadDesignerRef.value.newSetDataSource(JSON.stringify(bindingPathSchema), {})
- loading.value = false
- console.log('加载成功')
- //helpCopy(spread)
- handleBing()
- }, (err) => {
- loading.value = false
- console.log('加载失败', err)
- })
- } else {
- // 第一次创建模板的情况
- await initNewTemplate()
- }
- loading.value = false;
- }
- let temp = ref<string | null>(null)
- let index = ref<number>()
- const helpCopy = (spread) => {
- spread.bind(GC.Spread.Sheets.Events.ClipboardPasting, function (_e, info) {
- const cellRange = info.fromRange;
- if (info.sheet.getBindingPath(cellRange.row, cellRange.col)) {
- if (temp.value && temp.value.split('_')[0] !== info.sheet.getBindingPath(cellRange.row, cellRange.col).split('_')[0]) {
- index.value = undefined
- }
- temp.value = info.sheet.getBindingPath(cellRange.row, cellRange.col)
- if (!index.value){
- index.value = parseInt(info.sheet.getBindingPath(cellRange.row, cellRange.col).split('_')[1]|| 1 )+1
- }
- }
- })
- spread.bind(GC.Spread.Sheets.Events.ClipboardPasted, function (_e, info) {
- if (temp.value) {
- const arr = temp.value.split('_')
- const cellRange = info.cellRange;
- temp.value = arr[0] + '_' + index.value
- index.value = index.value + 1
- info.sheet.setBindingPath(cellRange.row, cellRange.col, temp.value)
- }
- })
- }
- const autoCopy = ref(false)
- watch(autoCopy,()=>{
- if (autoCopy.value){
- helpCopy(designer.getWorkbook())
- }else {
- handleUnbind()
- }
- })
- const handleUnbind = () => {
- const spread = designer.getWorkbook()
- spread.unbindAll()
- }
- // 初始化新模板
- const initNewTemplate = async () => {
- try {
- // 获取动态表格列数据
- const schemaSources = await getTbCols();
- // 构建初始的 bindingPathSchema
- const initialSchema = {
- type: "object",
- $schema: 'http://json-schema.org/draft-04/schema#',
- properties: {}
- };
- // 将动态表格列添加到 schema
- schemaSources?.forEach(element => {
- initialSchema.properties[element.col_code] = {
- dataFieldType: 'text',
- type: 'string'
- };
- });
- // 设置默认 schema 和空的绑定路径名称
- spreadDesignerRef.value.setDefaultSchema(JSON.stringify(initialSchema), '[]');
- // 设置空的数据源
- spreadDesignerRef.value.newSetDataSource(JSON.stringify(initialSchema), {});
- console.log('新模板初始化成功,动态列已添加到数据区');
- isNewTemplate = true;
- } catch (error) {
- console.error('初始化新模板失败:', error);
- ElMessage.error('初始化模板失败');
- }
- }
- let sheetTemp,colTemp,rowTemp
- const handleBing = () => {
- const spread = designer.getWorkbook()
- spread.sheets.forEach(sheet => {
- sheet.bind(GC.Spread.Sheets.Events.CellClick, (s, args) => {
- sheetTemp = args.sheetName
- colTemp = args.col
- rowTemp = args.row
- })
- });
- }
- const handleCopy = (value) => {
- if (sheetTemp != continuePageSheetName.value) {
- ElMessage.error('当前工作表不是续页');
- return
- }
- switch (value) {
- case 'lt':
- copyRangeTopLeft.value = colTemp + ',' + rowTemp
- break;
- case 'rt':
- copyRangeTopRight.value = colTemp + ',' + rowTemp
- break;
- case 'lb':
- copyRangeBottomLeft.value = colTemp + ',' + rowTemp
- break;
- case 'rb':
- copyRangeBottomRight.value = colTemp + ',' + rowTemp
- break;
- }
- }
- const handleConfig = async () => {
- isConfig.value = true
- // 查询配置
- let data = await DynamicTbApi.getDynamicTb(props.formData.id);
- console.log(data.copyConfig)
- if (data.copyConfig){
- const config = JSON.parse(data.copyConfig)
- isContinuePage.value = config.isContinuePage
- continuePageSheetName.value = config.sheetName
- copyRangeTopLeft.value = config.copyRange.topLeft
- copyRangeTopRight.value = config.copyRange.topRight
- copyRangeBottomLeft.value = config.copyRange.bottomLeft
- copyRangeBottomRight.value = config.copyRange.bottomRight
- isHiddenContinuePage.value = config.hidden
- }
- }
- const saveConfig = () => {
- if (!isContinuePage.value){
- DynamicTbApi.updateDynamicTb({
- id: props.formData.id,
- copyConfig: ""
- }).then(() => {
- ElMessage.success('保存成功');
- isConfig.value = false
- }).catch(() => {
- ElMessage.error('保存失败');
- })
- return;
- }
- // 检查判空
- if (!continuePageSheetName.value) {
- ElMessage.error('请选择续页工作表');
- return
- }
- if (!copyRangeTopLeft.value || !copyRangeTopRight.value || !copyRangeBottomLeft.value || !copyRangeBottomRight.value) {
- ElMessage.error('请选择复制范围');
- return;
- }
- DynamicTbApi.updateDynamicTb({
- id: props.formData.id,
- copyConfig: JSON.stringify({
- copyRange: {
- topLeft: copyRangeTopLeft.value,
- topRight: copyRangeTopRight.value,
- bottomLeft: copyRangeBottomLeft.value,
- bottomRight: copyRangeBottomRight.value
- },
- sheetName: continuePageSheetName.value,
- hidden: isHiddenContinuePage.value,
- isContinuePage: true
- })
- }).then(() => {
- ElMessage.success('保存成功');
- isConfig.value = false
- }).catch(() => {
- ElMessage.error('保存失败');
- })
- }
- </script>
- <style lang="scss" scoped>
- :deep(.default-toolbar) {
- padding-top: 0;
- }
- .lab-designer-container {
- position: relative;
- display: flex;
- flex-direction: row;
- align-items: stretch;
- height: calc(100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height));
- .spread-designer-container {
- width: calc(100% - 440px);
- height: 100%;
- padding: 0;
- }
- }
- // 配置弹窗的遮罩层允许点击穿透
- :deep(.config-dialog-modal) {
- pointer-events: none;
-
- // 确保弹窗本身可以接收鼠标事件
- .el-dialog {
- pointer-events: auto;
- }
- }
- </style>
|