|
|
@ -1,5 +1,160 @@ |
|
|
|
|
|
<template> |
|
|
|
|
|
<div v-loading="loading" class="da3"> |
|
|
|
|
|
<!-- <div class="toolbar"> |
|
|
|
|
|
<button @click="fillDemo">填入示例数据</button> |
|
|
|
|
|
<button @click="clearAll">清空</button> |
|
|
|
|
|
<button @click="exportJSON">导出当前填报 JSON</button> |
|
|
|
|
|
<button @click="exportExcel">导出 Excel</button> |
|
|
|
|
|
<span class="hint">数据驱动渲染 · 来自 template_item 等表</span> |
|
|
|
|
|
</div> --> |
|
|
|
|
|
|
|
|
|
|
|
<table v-if="data" class="form"> |
|
|
|
|
|
<!-- 附件2 + 题名 --> |
|
|
|
|
|
<tr><td class="attach" colspan="9">{{ data.template.attach }}</td></tr> |
|
|
|
|
|
<tr><td class="title" colspan="9">{{ data.template.title }}</td></tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 元信息(表号/制定机关/批准机关) --> |
|
|
|
|
|
<tr v-for="(m, i) in metaTop" :key="'mt' + i" class="meta-row"> |
|
|
|
|
|
<td colspan="9" class="meta"> |
|
|
|
|
|
<div class="meta-flex"> |
|
|
|
|
|
<!-- <i class="colon">:</i> --> |
|
|
|
|
|
<span class="meta-label">{{ m.label }}</span> |
|
|
|
|
|
<i class="colon">:</i> |
|
|
|
|
|
<span class="meta-value">{{ m.value }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 单位信息卡 01-07(左列 01-05 + 右列 06-07 + 批准文号/有效期) --> |
|
|
|
|
|
<tr v-for="(u, i) in leftUnits" :key="'lu' + i" :class="['unit-row', { 'unit-row-bordered': i >= 2 }]"> |
|
|
|
|
|
<td class="ucode">{{ u.code }}</td> |
|
|
|
|
|
<td class="ulabel" colspan="4">{{ u.label }}:<span v-if="isView" class="plain">{{ form['u_' + u.code] }}</span><input v-else v-model="form['u_' + u.code]" type="text"></td> |
|
|
|
|
|
|
|
|
|
|
|
<template v-if="i === 0"> |
|
|
|
|
|
<td class="meta" colspan="4"> |
|
|
|
|
|
<div class="meta-flex"> |
|
|
|
|
|
<span class="meta-label">批准文号</span> |
|
|
|
|
|
<i class="colon">:</i> |
|
|
|
|
|
<span class="meta-value">{{ data.template.approve_doc_no }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
</td></template> |
|
|
|
|
|
<template v-else-if="i === 1"> |
|
|
|
|
|
<td class="meta" colspan="4"> |
|
|
|
|
|
<div class="meta-flex"> |
|
|
|
|
|
<span class="meta-label">有效期至</span> |
|
|
|
|
|
<i class="colon">:</i> |
|
|
|
|
|
<span class="meta-value" style="text-align: right !important; text-align-last: right !important;">{{ data.template.valid_to }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
</td> |
|
|
|
|
|
</template> |
|
|
|
|
|
<template v-if="i === 2 && rightUnits[0]"> |
|
|
|
|
|
<td class="ucode">{{ rightUnits[0].code }}</td> |
|
|
|
|
|
<td class="ulabel" colspan="3">{{ rightUnits[0].label }}:<span v-if="isView" class="plain">{{ form['u_' + rightUnits[0].code] }}</span><input v-else v-model="form['u_' + rightUnits[0].code]" type="text"></td> |
|
|
|
|
|
</template> |
|
|
|
|
|
<template v-else-if="i === 3 && rightUnits[1]"> |
|
|
|
|
|
<td class="ucode ucell" rowspan="2">{{ rightUnits[1].code }}</td> |
|
|
|
|
|
<td class="ulabel ucell" colspan="3" rowspan="2">{{ rightUnits[1].label }}:<span v-if="isView" class="plain">{{ form['u_' + rightUnits[1].code] }}</span><input v-else v-model="form['u_' + rightUnits[1].code]" type="text"></td> |
|
|
|
|
|
</template> |
|
|
|
|
|
<template v-else-if="i === 4" /> |
|
|
|
|
|
<template v-else> |
|
|
|
|
|
<td colspan="4" /> |
|
|
|
|
|
</template> |
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 列头 --> |
|
|
|
|
|
<tr> |
|
|
|
|
|
<th class="colname" colspan="5">指标名称</th> |
|
|
|
|
|
<th class="colname">计量单位</th> |
|
|
|
|
|
<th class="colname">代码</th> |
|
|
|
|
|
<th class="colname">数量({{ data.template.cur_year }}年)</th> |
|
|
|
|
|
<th class="colname">上年度数据({{ data.template.prev_year }}年)</th> |
|
|
|
|
|
</tr> |
|
|
|
|
|
<tr> |
|
|
|
|
|
<th class="colsub" colspan="5">甲</th> |
|
|
|
|
|
<th class="colsub">乙</th> |
|
|
|
|
|
<th class="colsub">丙</th> |
|
|
|
|
|
<th class="colsub">1</th> |
|
|
|
|
|
<th class="colsub" /> |
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 主体 --> |
|
|
|
|
|
<template v-for="(it, idx) in data.body"> |
|
|
|
|
|
<!-- 章节 / 分组 --> |
|
|
|
|
|
<tr v-if="it.type === 'section' || it.type === 'group'" :key="'s' + idx" :class="it.type + ' body-row'"> |
|
|
|
|
|
<td class="nm" colspan="5" :style="{ paddingLeft: (it.level * 1.6) + 'em' }">{{ it.name }}</td> |
|
|
|
|
|
<td class="dash">{{ it.unit || '—' }}</td> |
|
|
|
|
|
<td class="dash">—</td> |
|
|
|
|
|
<td class="dash">—</td> |
|
|
|
|
|
<td class="dash" /> |
|
|
|
|
|
</tr> |
|
|
|
|
|
<!-- 指标 / 续行 --> |
|
|
|
|
|
<tr v-if="it.type !== 'section' && it.type !== 'group'" :key="'i' + idx" class="body-row"> |
|
|
|
|
|
<td class="nm" colspan="5" :style="{ paddingLeft: (it.level * 1.6) + 'em' }">{{ it.name }}</td> |
|
|
|
|
|
<td class="unit">{{ it.unit }}</td> |
|
|
|
|
|
<td class="code">{{ it.code }}</td> |
|
|
|
|
|
<td class="data"> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ form[it.code + '_c'] }}</span> |
|
|
|
|
|
<select v-else-if="it.input === 'yesno'" v-model="form[it.code + '_c']"> |
|
|
|
|
|
<option value="" /><option value="是">是</option><option value="否">否</option> |
|
|
|
|
|
</select> |
|
|
|
|
|
<input v-else-if="it.input === 'text'" v-model="form[it.code + '_c']" type="text"> |
|
|
|
|
|
<input v-else v-model="form[it.code + '_c']" type="text" inputmode="decimal"> |
|
|
|
|
|
</td> |
|
|
|
|
|
<td class="data"> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ form[it.code + '_p'] }}</span> |
|
|
|
|
|
<select v-else-if="it.input === 'yesno'" v-model="form[it.code + '_p']"> |
|
|
|
|
|
<option value="" /><option value="是">是</option><option value="否">否</option> |
|
|
|
|
|
</select> |
|
|
|
|
|
<input v-else-if="it.input === 'text'" v-model="form[it.code + '_p']" type="text"> |
|
|
|
|
|
<input v-else v-model="form[it.code + '_p']" type="text" inputmode="decimal"> |
|
|
|
|
|
</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 页脚签章 --> |
|
|
|
|
|
<tr class="footer-row"> |
|
|
|
|
|
<!-- 单位负责人 col1-4 --> |
|
|
|
|
|
<td colspan="4" class="footer-cell"> |
|
|
|
|
|
<span class="footer-label">单位负责人:</span> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ footerForm.unitHead }}</span> |
|
|
|
|
|
<input v-else v-model="footerForm.unitHead" type="text" class="footer-input"> |
|
|
|
|
|
</td> |
|
|
|
|
|
<!-- 统计负责人 col5 --> |
|
|
|
|
|
<td class="footer-cell"> |
|
|
|
|
|
<span class="footer-label">统计负责人:</span> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ footerForm.statsHead }}</span> |
|
|
|
|
|
<input v-else v-model="footerForm.statsHead" type="text" class="footer-input"> |
|
|
|
|
|
</td> |
|
|
|
|
|
<!-- 填表人 col6 --> |
|
|
|
|
|
<td class="footer-cell"> |
|
|
|
|
|
<span class="footer-label">填表人:</span> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ footerForm.filler }}</span> |
|
|
|
|
|
<input v-else v-model="footerForm.filler" type="text" class="footer-input"> |
|
|
|
|
|
</td> |
|
|
|
|
|
<!-- 报出时间 col7-9 --> |
|
|
|
|
|
<td colspan="3" class="footer-cell"> |
|
|
|
|
|
<span class="footer-label">报出时间:</span> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ footerForm.submitTime }}</span> |
|
|
|
|
|
<input v-else v-model="footerForm.submitTime" type="text" class="footer-input footer-input-wide" placeholder="20 年 月 日"> |
|
|
|
|
|
</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
</table> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 编辑模式:底部固定保存栏 --> |
|
|
|
|
|
<div v-if="!isView" class="save-bar"> |
|
|
|
|
|
<el-button class="saveetch-btn" type="primary" :loading="refetchLoading" @click="handleRefetch">重新获取统计数据</el-button> |
|
|
|
|
|
<el-button @click="exportExcel">导出Excel</el-button> |
|
|
|
|
|
<el-button @click="handleCancel">取消</el-button> |
|
|
|
|
|
<el-button class="save-btn" type="primary" :loading="saving" @click="handleSave">保存</el-button> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
import { FetchInitStatisticsTemplate, SaveStatisticsTemplate } from '@/api/statistics/statistics' |
|
|
import { FetchInitStatisticsTemplate, SaveStatisticsTemplate } from '@/api/statistics/statistics' |
|
|
|
|
|
import ExcelJS from 'exceljs' |
|
|
|
|
|
import { saveAs } from 'file-saver' |
|
|
|
|
|
import { mapGetters } from 'vuex' |
|
|
|
|
|
|
|
|
export default { |
|
|
export default { |
|
|
name: 'Da3Report', |
|
|
name: 'Da3Report', |
|
|
@ -12,10 +167,18 @@ export default { |
|
|
rawReport: null, |
|
|
rawReport: null, |
|
|
loading: false, |
|
|
loading: false, |
|
|
saving: false, |
|
|
saving: false, |
|
|
refetchLoading: false |
|
|
|
|
|
|
|
|
refetchLoading: false, |
|
|
|
|
|
// 页脚签章:单位负责人 / 统计负责人 / 填表人 / 报出时间 |
|
|
|
|
|
footerForm: { |
|
|
|
|
|
unitHead: '', |
|
|
|
|
|
statsHead: '', |
|
|
|
|
|
filler: '', |
|
|
|
|
|
submitTime: '' |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
computed: { |
|
|
computed: { |
|
|
|
|
|
...mapGetters(['user']), |
|
|
data() { |
|
|
data() { |
|
|
return this.remoteModel |
|
|
return this.remoteModel |
|
|
}, |
|
|
}, |
|
|
@ -49,6 +212,12 @@ export default { |
|
|
this.initForm(d) |
|
|
this.initForm(d) |
|
|
// 接口模型自带填报值时回显 |
|
|
// 接口模型自带填报值时回显 |
|
|
if (d.formData) this.fillFormData(d.formData) |
|
|
if (d.formData) this.fillFormData(d.formData) |
|
|
|
|
|
// 回显页脚签章 |
|
|
|
|
|
if (d.footerForm) { |
|
|
|
|
|
Object.keys(this.footerForm).forEach(k => { |
|
|
|
|
|
this.footerForm[k] = d.footerForm[k] != null ? d.footerForm[k] : '' |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
immediate: true |
|
|
immediate: true |
|
|
} |
|
|
} |
|
|
@ -203,15 +372,25 @@ export default { |
|
|
prev_year: res.prevYear |
|
|
prev_year: res.prevYear |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 6. 页脚签章(值取自顶层字段) |
|
|
|
|
|
|
|
|
// 6. 页脚签章(值取自顶层字段,同时存入 formData 用于回填) |
|
|
|
|
|
const footerFormData = { |
|
|
|
|
|
unitHead: res.unitHead || '', |
|
|
|
|
|
statsHead: res.statsHead || '', |
|
|
|
|
|
filler: res.filler || '', |
|
|
|
|
|
submitTime: res.submitTime || '' |
|
|
|
|
|
} |
|
|
|
|
|
// 填表人默认取当前登录用户名(接口无值时) |
|
|
|
|
|
if (!footerFormData.filler && this.user && this.user.username) { |
|
|
|
|
|
footerFormData.filler = this.user.username |
|
|
|
|
|
} |
|
|
const footer = [ |
|
|
const footer = [ |
|
|
{ col: 1, label: '单位负责人:' + (res.unitHead || '') }, |
|
|
|
|
|
{ col: 5, label: '统计负责人:' + (res.statsHead || '') }, |
|
|
|
|
|
{ col: 6, label: '填表人:' + (res.filler || '') }, |
|
|
|
|
|
{ col: 8, label: '报出时间:' + (res.submitTime || '') } |
|
|
|
|
|
|
|
|
{ col: 1, label: '单位负责人:' + footerFormData.unitHead }, |
|
|
|
|
|
{ col: 5, label: '统计负责人:' + footerFormData.statsHead }, |
|
|
|
|
|
{ col: 6, label: '填表人:' + footerFormData.filler }, |
|
|
|
|
|
{ col: 8, label: '报出时间:' + footerFormData.submitTime } |
|
|
] |
|
|
] |
|
|
|
|
|
|
|
|
return { template, units, body, footer, formData } |
|
|
|
|
|
|
|
|
return { template, units, body, footer, formData, footerForm: footerFormData } |
|
|
}, |
|
|
}, |
|
|
// 回填填报值:{ u_01: 'xx', '08_c': '12' } 或 { units: {01:'xx'}, data: {08:{cur:'12'}} } |
|
|
// 回填填报值:{ u_01: 'xx', '08_c': '12' } 或 { units: {01:'xx'}, data: {08:{cur:'12'}} } |
|
|
fillFormData(formData) { |
|
|
fillFormData(formData) { |
|
|
@ -229,6 +408,12 @@ export default { |
|
|
this.$set(this.form, code + '_p', item.prev) |
|
|
this.$set(this.form, code + '_p', item.prev) |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
// 回填页脚签章 |
|
|
|
|
|
if (formData.footer) { |
|
|
|
|
|
Object.keys(this.footerForm).forEach(k => { |
|
|
|
|
|
if (formData.footer[k] != null) this.footerForm[k] = formData.footer[k] |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
Object.keys(formData).forEach(k => { |
|
|
Object.keys(formData).forEach(k => { |
|
|
if (Object.prototype.hasOwnProperty.call(this.form, k)) { |
|
|
if (Object.prototype.hasOwnProperty.call(this.form, k)) { |
|
|
this.$set(this.form, k, formData[k]) |
|
|
this.$set(this.form, k, formData[k]) |
|
|
@ -272,13 +457,239 @@ export default { |
|
|
reportData[it.code] = { cur: this.form[it.code + '_c'] || '', prev: this.form[it.code + '_p'] || '' } |
|
|
reportData[it.code] = { cur: this.form[it.code + '_c'] || '', prev: this.form[it.code + '_p'] || '' } |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
return { units, data: reportData } |
|
|
|
|
|
|
|
|
return { units, data: reportData, footer: { ...this.footerForm }} |
|
|
}, |
|
|
}, |
|
|
exportJSON() { |
|
|
exportJSON() { |
|
|
const payload = this.buildPayload() |
|
|
const payload = this.buildPayload() |
|
|
console.log(JSON.stringify(payload, null, 2)) |
|
|
console.log(JSON.stringify(payload, null, 2)) |
|
|
this.$emit('export', payload) |
|
|
this.$emit('export', payload) |
|
|
alert('已导出到控制台(F12),并通过 emit("export") 回传父组件') |
|
|
|
|
|
|
|
|
// alert('已导出到控制台(F12),并通过 emit("export") 回传父组件') |
|
|
|
|
|
}, |
|
|
|
|
|
// 导出 Excel |
|
|
|
|
|
async exportExcel() { |
|
|
|
|
|
if (!this.data) { |
|
|
|
|
|
this.$message.warning('数据未加载完成,请稍后') |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const wb = new ExcelJS.Workbook() |
|
|
|
|
|
const ws = wb.addWorksheet('档案室基本情况表') |
|
|
|
|
|
|
|
|
|
|
|
// 9 列布局:A-E 指标名称(合并) F计量单位 G代码 H本年 I上年 |
|
|
|
|
|
ws.columns = [ |
|
|
|
|
|
{ width: 4 }, // A |
|
|
|
|
|
{ width: 4 }, // B |
|
|
|
|
|
{ width: 4 }, // C |
|
|
|
|
|
{ width: 4 }, // D |
|
|
|
|
|
{ width: 4 }, // E |
|
|
|
|
|
{ width: 10 }, // F - 计量单位 |
|
|
|
|
|
{ width: 8 }, // G - 代码 |
|
|
|
|
|
{ width: 14 }, // H - 本年 |
|
|
|
|
|
{ width: 14 } // I - 上年 |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第1行:附件2 ===== |
|
|
|
|
|
ws.getCell('A1').value = '附件2' |
|
|
|
|
|
ws.getCell('A1').font = { size: 12 } |
|
|
|
|
|
ws.getCell('A1').alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
ws.getRow(1).height = 18 |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第2行:标题 ===== |
|
|
|
|
|
ws.mergeCells('A2:I2') |
|
|
|
|
|
const titleCell = ws.getCell('A2') |
|
|
|
|
|
titleCell.value = this.data.template.title |
|
|
|
|
|
titleCell.font = { size: 22, bold: true, name: '黑体' } |
|
|
|
|
|
titleCell.alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
ws.getRow(2).height = 35 |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第3行:表号(右对齐) ===== |
|
|
|
|
|
ws.getCell('I3').value = `表号:${this.data.template.form_no}` |
|
|
|
|
|
ws.getCell('I3').font = { size: 10 } |
|
|
|
|
|
ws.getCell('I3').alignment = { horizontal: 'right', vertical: 'middle' } |
|
|
|
|
|
ws.getRow(3).height = 16 |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第4行:制定机关(右对齐) ===== |
|
|
|
|
|
ws.getCell('I4').value = `制定机关:${this.data.template.issuing_org}` |
|
|
|
|
|
ws.getCell('I4').font = { size: 10 } |
|
|
|
|
|
ws.getCell('I4').alignment = { horizontal: 'right', vertical: 'middle' } |
|
|
|
|
|
ws.getRow(4).height = 16 |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第5行:批准机关(右对齐) ===== |
|
|
|
|
|
ws.getCell('I5').value = `批准机关:${this.data.template.approving_org}` |
|
|
|
|
|
ws.getCell('I5').font = { size: 10 } |
|
|
|
|
|
ws.getCell('I5').alignment = { horizontal: 'right', vertical: 'middle' } |
|
|
|
|
|
ws.getRow(5).height = 16 |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第6-10行:单位信息(左右两栏) ===== |
|
|
|
|
|
const units = this.data.units || [] |
|
|
|
|
|
// const leftUnits = units.filter(u => u.card === 1) // 01-05 |
|
|
|
|
|
// const rightUnits = units.filter(u => u.card === 2) // 06-07 |
|
|
|
|
|
const unitValueMap = {} |
|
|
|
|
|
units.forEach(u => { unitValueMap[u.code] = this.form['u_' + u.code] || '' }) |
|
|
|
|
|
|
|
|
|
|
|
// 左侧 01-05(A-E列),右侧 06-07(F-I列) |
|
|
|
|
|
const leftRows = [ |
|
|
|
|
|
{ code: '01', label: '统一社会信用代码:' }, |
|
|
|
|
|
{ code: '02', label: '单位名称:' }, |
|
|
|
|
|
{ code: '03', label: '单位类别代码:' }, |
|
|
|
|
|
{ code: '04', label: '邮政编码:' }, |
|
|
|
|
|
{ code: '05', label: '单位地址:' } |
|
|
|
|
|
] |
|
|
|
|
|
const rightRows = [ |
|
|
|
|
|
{ code: '06', label: '电话:' }, |
|
|
|
|
|
{ code: '07', label: '机构情况:' } |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
leftRows.forEach((item, idx) => { |
|
|
|
|
|
const r = 6 + idx |
|
|
|
|
|
// 左侧代码 A列 |
|
|
|
|
|
ws.getCell(`A${r}`).value = item.code |
|
|
|
|
|
ws.getCell(`A${r}`).font = { size: 10, bold: true } |
|
|
|
|
|
ws.getCell(`A${r}`).alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
// 左侧标签 B-E列 |
|
|
|
|
|
ws.mergeCells(`B${r}:E${r}`) |
|
|
|
|
|
ws.getCell(`B${r}`).value = item.label + unitValueMap[item.code] |
|
|
|
|
|
ws.getCell(`B${r}`).font = { size: 10 } |
|
|
|
|
|
ws.getCell(`B${r}`).alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
ws.getRow(r).height = 16 |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// 右侧 06-07 从第8行开始 |
|
|
|
|
|
rightRows.forEach((item, idx) => { |
|
|
|
|
|
const r = 8 + idx |
|
|
|
|
|
ws.getCell(`F${r}`).value = item.code |
|
|
|
|
|
ws.getCell(`F${r}`).font = { size: 10, bold: true } |
|
|
|
|
|
ws.getCell(`F${r}`).alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
ws.mergeCells(`G${r}:I${r}`) |
|
|
|
|
|
ws.getCell(`G${r}`).value = item.label + unitValueMap[item.code] |
|
|
|
|
|
ws.getCell(`G${r}`).font = { size: 10 } |
|
|
|
|
|
ws.getCell(`G${r}`).alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// 批准文号、有效期至(右侧第6、7行) |
|
|
|
|
|
ws.getCell('I6').value = `批准文号:${this.data.template.approve_doc_no}` |
|
|
|
|
|
ws.getCell('I6').font = { size: 10 } |
|
|
|
|
|
ws.getCell('I6').alignment = { horizontal: 'right', vertical: 'middle' } |
|
|
|
|
|
ws.getCell('I7').value = `有效期至:${this.data.template.valid_to}` |
|
|
|
|
|
ws.getCell('I7').font = { size: 10 } |
|
|
|
|
|
ws.getCell('I7').alignment = { horizontal: 'right', vertical: 'middle' } |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第11行:表头(指标名称合并A-E) ===== |
|
|
|
|
|
ws.mergeCells('A11:E11') |
|
|
|
|
|
ws.getCell('A11').value = '指标名称' |
|
|
|
|
|
ws.getCell('F11').value = '计量单位' |
|
|
|
|
|
ws.getCell('G11').value = '代码' |
|
|
|
|
|
ws.getCell('H11').value = `数量(${this.data.template.cur_year}年)` |
|
|
|
|
|
ws.getCell('I11').value = `上年度数据(${this.data.template.prev_year}年)` |
|
|
|
|
|
const headerRow = ws.getRow(11) |
|
|
|
|
|
for (let c = 1; c <= 9; c++) { |
|
|
|
|
|
const cell = headerRow.getCell(c) |
|
|
|
|
|
cell.font = { size: 10, bold: true } |
|
|
|
|
|
cell.alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
cell.border = { |
|
|
|
|
|
top: { style: 'thin' }, bottom: { style: 'thin' }, |
|
|
|
|
|
left: { style: 'thin' }, right: { style: 'thin' } |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
ws.getRow(11).height = 20 |
|
|
|
|
|
|
|
|
|
|
|
// ===== 第12行:甲乙丙1 ===== |
|
|
|
|
|
ws.mergeCells('A12:E12') |
|
|
|
|
|
ws.getCell('A12').value = '甲' |
|
|
|
|
|
ws.getCell('F12').value = '乙' |
|
|
|
|
|
ws.getCell('G12').value = '丙' |
|
|
|
|
|
ws.getCell('H12').value = '1' |
|
|
|
|
|
ws.getCell('I12').value = '' |
|
|
|
|
|
const subHeaderRow = ws.getRow(12) |
|
|
|
|
|
for (let c = 1; c <= 9; c++) { |
|
|
|
|
|
const cell = subHeaderRow.getCell(c) |
|
|
|
|
|
cell.font = { size: 10 } |
|
|
|
|
|
cell.alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
cell.border = { |
|
|
|
|
|
top: { style: 'thin' }, bottom: { style: 'thin' }, |
|
|
|
|
|
left: { style: 'thin' }, right: { style: 'thin' } |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
ws.getRow(12).height = 18 |
|
|
|
|
|
|
|
|
|
|
|
// ===== 数据行:从第13行开始 ===== |
|
|
|
|
|
const body = this.data.body || [] |
|
|
|
|
|
let rowIdx = 13 |
|
|
|
|
|
|
|
|
|
|
|
body.forEach(it => { |
|
|
|
|
|
const row = ws.getRow(rowIdx) |
|
|
|
|
|
const indent = ' '.repeat(it.level || 0) |
|
|
|
|
|
const name = it.type === 'section' || it.type === 'group' ? it.name : indent + it.name |
|
|
|
|
|
|
|
|
|
|
|
// A-E 合并为指标名称 |
|
|
|
|
|
ws.mergeCells(`A${rowIdx}:E${rowIdx}`) |
|
|
|
|
|
row.getCell(1).value = name |
|
|
|
|
|
row.getCell(6).value = it.unit || '—' // F - 计量单位 |
|
|
|
|
|
row.getCell(7).value = it.code || '' // G - 代码 |
|
|
|
|
|
row.getCell(8).value = this.form[it.code + '_c'] || '' // H - 本年 |
|
|
|
|
|
row.getCell(9).value = this.form[it.code + '_p'] || '' // I - 上年 |
|
|
|
|
|
|
|
|
|
|
|
// 样式 |
|
|
|
|
|
row.font = { size: 10 } |
|
|
|
|
|
row.getCell(1).alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
row.getCell(6).alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
row.getCell(7).alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
row.getCell(8).alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
row.getCell(9).alignment = { horizontal: 'center', vertical: 'middle' } |
|
|
|
|
|
|
|
|
|
|
|
// section/group 加粗 |
|
|
|
|
|
if (it.type === 'section' || it.type === 'group') { |
|
|
|
|
|
row.font = { size: 10, bold: true } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 边框(A-I 共9列) |
|
|
|
|
|
for (let c = 1; c <= 9; c++) { |
|
|
|
|
|
row.getCell(c).border = { |
|
|
|
|
|
top: { style: 'thin' }, bottom: { style: 'thin' }, |
|
|
|
|
|
left: { style: 'thin' }, right: { style: 'thin' } |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
row.height = 16 |
|
|
|
|
|
rowIdx++ |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// ===== 页脚:单位负责人 | 统计负责人 | 填表人 | 报出时间 ===== |
|
|
|
|
|
rowIdx++ |
|
|
|
|
|
// 单位负责人(A-D 合并) |
|
|
|
|
|
ws.mergeCells(`A${rowIdx}:D${rowIdx}`) |
|
|
|
|
|
const unitHeadCell = ws.getCell(`A${rowIdx}`) |
|
|
|
|
|
unitHeadCell.value = '单位负责人:' + (this.footerForm.unitHead || '') |
|
|
|
|
|
unitHeadCell.font = { size: 10 } |
|
|
|
|
|
unitHeadCell.alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
|
|
|
|
|
|
// 统计负责人(E 列) |
|
|
|
|
|
const statsHeadCell = ws.getCell(`E${rowIdx}`) |
|
|
|
|
|
statsHeadCell.value = '统计负责人:' + (this.footerForm.statsHead || '') |
|
|
|
|
|
statsHeadCell.font = { size: 10 } |
|
|
|
|
|
statsHeadCell.alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
|
|
|
|
|
|
// 填表人(F-G 合并) |
|
|
|
|
|
ws.mergeCells(`F${rowIdx}:G${rowIdx}`) |
|
|
|
|
|
const fillerCell = ws.getCell(`F${rowIdx}`) |
|
|
|
|
|
fillerCell.value = '填表人:' + (this.footerForm.filler || '') |
|
|
|
|
|
fillerCell.font = { size: 10 } |
|
|
|
|
|
fillerCell.alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
|
|
|
|
|
|
// 报出时间(H-I 合并) |
|
|
|
|
|
ws.mergeCells(`H${rowIdx}:I${rowIdx}`) |
|
|
|
|
|
const submitTimeCell = ws.getCell(`H${rowIdx}`) |
|
|
|
|
|
submitTimeCell.value = '报出时间:' + (this.footerForm.submitTime || '') |
|
|
|
|
|
submitTimeCell.font = { size: 10 } |
|
|
|
|
|
submitTimeCell.alignment = { horizontal: 'left', vertical: 'middle' } |
|
|
|
|
|
|
|
|
|
|
|
const footerRow = ws.getRow(rowIdx) |
|
|
|
|
|
footerRow.height = 20 |
|
|
|
|
|
|
|
|
|
|
|
// 生成并下载 |
|
|
|
|
|
const buffer = await wb.xlsx.writeBuffer() |
|
|
|
|
|
const fileName = `${this.data.template.title}_${this.data.template.cur_year}年.xlsx` |
|
|
|
|
|
saveAs(new Blob([buffer]), fileName) |
|
|
}, |
|
|
}, |
|
|
// 保存:基于接口原始结构回填用户输入(数值也写入 text 字段,与回显口径一致) |
|
|
// 保存:基于接口原始结构回填用户输入(数值也写入 text 字段,与回显口径一致) |
|
|
handleSave() { |
|
|
handleSave() { |
|
|
@ -324,6 +735,12 @@ export default { |
|
|
} |
|
|
} |
|
|
walk(payload.rows) |
|
|
walk(payload.rows) |
|
|
|
|
|
|
|
|
|
|
|
// 页脚签章回填到顶层字段 |
|
|
|
|
|
payload.unitHead = this.footerForm.unitHead || null |
|
|
|
|
|
payload.statsHead = this.footerForm.statsHead || null |
|
|
|
|
|
payload.filler = this.footerForm.filler || null |
|
|
|
|
|
payload.submitTime = this.footerForm.submitTime || null |
|
|
|
|
|
|
|
|
this.saving = true |
|
|
this.saving = true |
|
|
SaveStatisticsTemplate(payload).then(() => { |
|
|
SaveStatisticsTemplate(payload).then(() => { |
|
|
this.$message({ message: '保存成功', type: 'success', offset: 80 }) |
|
|
this.$message({ message: '保存成功', type: 'success', offset: 80 }) |
|
|
@ -344,133 +761,6 @@ export default { |
|
|
} |
|
|
} |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
<template> |
|
|
|
|
|
<div v-loading="loading" class="da3"> |
|
|
|
|
|
<!-- <div class="toolbar"> |
|
|
|
|
|
<button @click="fillDemo">填入示例数据</button> |
|
|
|
|
|
<button @click="clearAll">清空</button> |
|
|
|
|
|
<button @click="exportJSON">导出当前填报 JSON</button> |
|
|
|
|
|
<span class="hint">数据驱动渲染 · 来自 template_item 等表</span> |
|
|
|
|
|
</div> --> |
|
|
|
|
|
|
|
|
|
|
|
<table v-if="data" class="form"> |
|
|
|
|
|
<!-- 附件2 + 题名 --> |
|
|
|
|
|
<tr><td class="attach" colspan="9">{{ data.template.attach }}</td></tr> |
|
|
|
|
|
<tr><td class="title" colspan="9">{{ data.template.title }}</td></tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 元信息(表号/制定机关/批准机关) --> |
|
|
|
|
|
<tr v-for="(m, i) in metaTop" :key="'mt' + i" class="meta-row"> |
|
|
|
|
|
<td colspan="9" class="meta"> |
|
|
|
|
|
<div class="meta-flex"> |
|
|
|
|
|
<!-- <i class="colon">:</i> --> |
|
|
|
|
|
<span class="meta-label">{{ m.label }}</span> |
|
|
|
|
|
<i class="colon">:</i> |
|
|
|
|
|
<span class="meta-value">{{ m.value }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 单位信息卡 01-07(左列 01-05 + 右列 06-07 + 批准文号/有效期) --> |
|
|
|
|
|
<tr v-for="(u, i) in leftUnits" :key="'lu' + i" :class="['unit-row', { 'unit-row-bordered': i >= 2 }]"> |
|
|
|
|
|
<td class="ucode">{{ u.code }}</td> |
|
|
|
|
|
<td class="ulabel" colspan="4">{{ u.label }}:<span v-if="isView" class="plain">{{ form['u_' + u.code] }}</span><input v-else v-model="form['u_' + u.code]" type="text"></td> |
|
|
|
|
|
|
|
|
|
|
|
<template v-if="i === 0"> |
|
|
|
|
|
<td class="meta" colspan="4"> |
|
|
|
|
|
<div class="meta-flex"> |
|
|
|
|
|
<span class="meta-label">批准文号</span> |
|
|
|
|
|
<i class="colon">:</i> |
|
|
|
|
|
<span class="meta-value">{{ data.template.approve_doc_no }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
</td></template> |
|
|
|
|
|
<template v-else-if="i === 1"> |
|
|
|
|
|
<td class="meta" colspan="4"> |
|
|
|
|
|
<div class="meta-flex"> |
|
|
|
|
|
<span class="meta-label">有效期至</span> |
|
|
|
|
|
<i class="colon">:</i> |
|
|
|
|
|
<span class="meta-value" style="text-align: right !important; text-align-last: right !important;">{{ data.template.valid_to }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
</td> |
|
|
|
|
|
</template> |
|
|
|
|
|
<template v-if="i === 2 && rightUnits[0]"> |
|
|
|
|
|
<td class="ucode">{{ rightUnits[0].code }}</td> |
|
|
|
|
|
<td class="ulabel" colspan="3">{{ rightUnits[0].label }}:<span v-if="isView" class="plain">{{ form['u_' + rightUnits[0].code] }}</span><input v-else v-model="form['u_' + rightUnits[0].code]" type="text"></td> |
|
|
|
|
|
</template> |
|
|
|
|
|
<template v-else-if="i === 3 && rightUnits[1]"> |
|
|
|
|
|
<td class="ucode ucell" rowspan="2">{{ rightUnits[1].code }}</td> |
|
|
|
|
|
<td class="ulabel ucell" colspan="3" rowspan="2">{{ rightUnits[1].label }}:<span v-if="isView" class="plain">{{ form['u_' + rightUnits[1].code] }}</span><input v-else v-model="form['u_' + rightUnits[1].code]" type="text"></td> |
|
|
|
|
|
</template> |
|
|
|
|
|
<template v-else-if="i === 4" /> |
|
|
|
|
|
<template v-else> |
|
|
|
|
|
<td colspan="4" /> |
|
|
|
|
|
</template> |
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 列头 --> |
|
|
|
|
|
<tr> |
|
|
|
|
|
<th class="colname" colspan="5">指标名称</th> |
|
|
|
|
|
<th class="colname">计量单位</th> |
|
|
|
|
|
<th class="colname">代码</th> |
|
|
|
|
|
<th class="colname">数量({{ data.template.cur_year }}年)</th> |
|
|
|
|
|
<th class="colname">上年度数据({{ data.template.prev_year }}年)</th> |
|
|
|
|
|
</tr> |
|
|
|
|
|
<tr> |
|
|
|
|
|
<th class="colsub" colspan="5">甲</th> |
|
|
|
|
|
<th class="colsub">乙</th> |
|
|
|
|
|
<th class="colsub">丙</th> |
|
|
|
|
|
<th class="colsub">1</th> |
|
|
|
|
|
<th class="colsub" /> |
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 主体 --> |
|
|
|
|
|
<template v-for="(it, idx) in data.body"> |
|
|
|
|
|
<!-- 章节 / 分组 --> |
|
|
|
|
|
<tr v-if="it.type === 'section' || it.type === 'group'" :key="'s' + idx" :class="it.type + ' body-row'"> |
|
|
|
|
|
<td class="nm" colspan="5" :style="{ paddingLeft: (it.level * 1.6) + 'em' }">{{ it.name }}</td> |
|
|
|
|
|
<td class="dash">{{ it.unit || '—' }}</td> |
|
|
|
|
|
<td class="dash">—</td> |
|
|
|
|
|
<td class="dash">—</td> |
|
|
|
|
|
<td class="dash" /> |
|
|
|
|
|
</tr> |
|
|
|
|
|
<!-- 指标 / 续行 --> |
|
|
|
|
|
<tr v-if="it.type !== 'section' && it.type !== 'group'" :key="'i' + idx" class="body-row"> |
|
|
|
|
|
<td class="nm" colspan="5" :style="{ paddingLeft: (it.level * 1.6) + 'em' }">{{ it.name }}</td> |
|
|
|
|
|
<td class="unit">{{ it.unit }}</td> |
|
|
|
|
|
<td class="code">{{ it.code }}</td> |
|
|
|
|
|
<td class="data"> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ form[it.code + '_c'] }}</span> |
|
|
|
|
|
<select v-else-if="it.input === 'yesno'" v-model="form[it.code + '_c']"> |
|
|
|
|
|
<option value="" /><option value="是">是</option><option value="否">否</option> |
|
|
|
|
|
</select> |
|
|
|
|
|
<input v-else-if="it.input === 'text'" v-model="form[it.code + '_c']" type="text"> |
|
|
|
|
|
<input v-else v-model="form[it.code + '_c']" type="text" inputmode="decimal"> |
|
|
|
|
|
</td> |
|
|
|
|
|
<td class="data"> |
|
|
|
|
|
<span v-if="isView" class="plain">{{ form[it.code + '_p'] }}</span> |
|
|
|
|
|
<select v-else-if="it.input === 'yesno'" v-model="form[it.code + '_p']"> |
|
|
|
|
|
<option value="" /><option value="是">是</option><option value="否">否</option> |
|
|
|
|
|
</select> |
|
|
|
|
|
<input v-else-if="it.input === 'text'" v-model="form[it.code + '_p']" type="text"> |
|
|
|
|
|
<input v-else v-model="form[it.code + '_p']" type="text" inputmode="decimal"> |
|
|
|
|
|
</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 页脚签章 --> |
|
|
|
|
|
<tr class="footer-row"> |
|
|
|
|
|
<td v-for="(f, fi) in data.footer" :key="'f' + fi" :colspan="fi < data.footer.length - 1 ? data.footer[fi + 1].col - f.col : (10 - f.col)">{{ f.label }}</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
</table> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 编辑模式:底部固定保存栏 --> |
|
|
|
|
|
<div v-if="!isView" class="save-bar"> |
|
|
|
|
|
<el-button class="saveetch-btn" type="primary" :loading="refetchLoading" @click="handleRefetch">重新获取统计数据</el-button> |
|
|
|
|
|
<el-button @click="handleCancel">取消</el-button> |
|
|
|
|
|
<el-button class="save-btn" type="primary" :loading="saving" @click="handleSave">保存</el-button> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
|
|
<style scoped> |
|
|
<style scoped> |
|
|
* { box-sizing: border-box; } |
|
|
* { box-sizing: border-box; } |
|
|
.da3 { font-family: "SimSun", "宋体", serif; color: #000; background: #fff; padding: 20px 20px 76px; } |
|
|
.da3 { font-family: "SimSun", "宋体", serif; color: #000; background: #fff; padding: 20px 20px 76px; } |
|
|
@ -537,6 +827,10 @@ table.form td.title { font-size: 30px !important; font-weight: bold; text-align: |
|
|
/* 查看模式纯文本展示 */ |
|
|
/* 查看模式纯文本展示 */ |
|
|
.plain { display: inline-block; min-width: 60px; min-height: 1em; } |
|
|
.plain { display: inline-block; min-width: 60px; min-height: 1em; } |
|
|
.dash { text-align: center; } |
|
|
.dash { text-align: center; } |
|
|
.footer-row td { border: none; border-top: 1px solid #000; white-space: nowrap; padding: 8px 6px; } |
|
|
|
|
|
|
|
|
.footer-row td { border: none; border-top: 1px solid #000; padding: 8px 6px; } |
|
|
|
|
|
.footer-cell { white-space: nowrap; } |
|
|
|
|
|
.footer-label { white-space: nowrap; } |
|
|
|
|
|
.footer-input { border: 1px solid #ccc; font-family: inherit; font-size: 13px; padding: 2px; outline: none; width: 70px; display: inline-block; vertical-align: middle; } |
|
|
|
|
|
.footer-input-wide { width: 120px; } |
|
|
.footer-note td { border: none; padding: 8px 6px; font-size: 12px; } |
|
|
.footer-note td { border: none; padding: 8px 6px; font-size: 12px; } |
|
|
</style> |
|
|
</style> |