This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
<script setup lang="ts">
|
||||
/** 事项编辑(22 字段 + 表单/流程绑定;迁移自 CMS ServiceItemEdit,siteId→siteCode)。 */
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { Message } from "view-ui-plus";
|
||||
import {
|
||||
fileApi,
|
||||
svcDictApi,
|
||||
svcFlowApi,
|
||||
svcFormApi,
|
||||
svcItemApi,
|
||||
} from "@/api/module/svc";
|
||||
import type { SvcDeptVo, SvcFormVo, SvcItemAttachmentVo, SvcItemSaveRequest } from "@/types/svc";
|
||||
import config from "@/assets/config/host.config";
|
||||
import { useSite } from "@/utils/site";
|
||||
import RichTextEditor from "@/components/RichTextEditor.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { currentSiteCode, loadSites } = useSite();
|
||||
|
||||
const form = ref<SvcItemSaveRequest & Record<string, any>>({
|
||||
siteCode: "",
|
||||
itemCode: "",
|
||||
title: "",
|
||||
handleMode: 1,
|
||||
onlineFlag: 1,
|
||||
sortNo: 0,
|
||||
});
|
||||
const objects = ref<{ dictKey: string; dictName: string }[]>([]);
|
||||
const categories = ref<{ dictKey: string; dictName: string }[]>([]);
|
||||
const depts = ref<SvcDeptVo[]>([]);
|
||||
const forms = ref<SvcFormVo[]>([]);
|
||||
const flows = ref<{ flowGuid: string; name: string; version: number }[]>([]);
|
||||
const flowDisabled = ref(false);
|
||||
const flowDisabledReason = ref("");
|
||||
const attachments = ref<SvcItemAttachmentVo[]>([]);
|
||||
const uploadingCover = ref(false);
|
||||
|
||||
const editing = computed(() => !!route.query.id);
|
||||
const pageTitle = computed(() => (editing.value ? "编辑事项" : "新建事项"));
|
||||
|
||||
/** 部门树扁平化(缩进展示,承袭 CMS flattenDepts) */
|
||||
const deptOptions = computed(() => {
|
||||
const out: { id: number; label: string }[] = [];
|
||||
const walk = (nodes: SvcDeptVo[], depth: number) => {
|
||||
for (const n of nodes || []) {
|
||||
out.push({ id: n.id, label: `${"\u00A0\u00A0\u00A0\u00A0".repeat(depth)}${n.name}` });
|
||||
walk(n.children || [], depth + 1);
|
||||
}
|
||||
};
|
||||
walk(depts.value, 0);
|
||||
return out;
|
||||
});
|
||||
|
||||
async function loadAll() {
|
||||
await loadSites();
|
||||
const siteCode = currentSiteCode.value;
|
||||
if (!siteCode) {
|
||||
Message.warning("无可管理的站点,请先在 UserBase 配置应用模块并开通站点");
|
||||
return;
|
||||
}
|
||||
form.value.siteCode = siteCode;
|
||||
objects.value = ((await svcDictApi.list(siteCode, "object").send()) as any[]) || [];
|
||||
categories.value = ((await svcDictApi.list(siteCode, "category").send()) as any[]) || [];
|
||||
depts.value = (await svcDictApi.deptTree(siteCode).send()) || [];
|
||||
forms.value = ((await svcFormApi.list(siteCode).send()) as SvcFormVo[]) || [];
|
||||
try {
|
||||
flows.value = ((await svcFlowApi.list(siteCode).send()) as any[]) || [];
|
||||
flowDisabled.value = false;
|
||||
flowDisabledReason.value = "";
|
||||
} catch (e: any) {
|
||||
// BP-R4-2:仅真正"流程中心未接入"(svchall.flow.enabled=false)才提示未接入;
|
||||
// 站点/鉴权/网络类错误按原始信息提示,避免排障方向被误导
|
||||
flowDisabled.value = true;
|
||||
const msg = String(e?.message || e || "");
|
||||
flowDisabledReason.value = msg.includes("流程中心未接入")
|
||||
? "流程中心未接入,暂只能配置线下事项"
|
||||
: `流程列表获取失败:${msg}`;
|
||||
}
|
||||
if (route.query.id) {
|
||||
const detail: any = await svcItemApi.get(siteCode, Number(route.query.id)).send();
|
||||
form.value = { ...form.value, ...detail };
|
||||
attachments.value = (await svcItemApi.attList(siteCode, Number(route.query.id)).send()) || [];
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadCover(file: File) {
|
||||
uploadingCover.value = true;
|
||||
try {
|
||||
const res: any = await fileApi.upload(file, "image", currentSiteCode.value).send();
|
||||
form.value.coverImage = res.fileUrl;
|
||||
} finally {
|
||||
uploadingCover.value = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function uploadGuide(file: File) {
|
||||
const res: any = await fileApi.upload(file, "file", currentSiteCode.value).send();
|
||||
attachments.value.push({ id: res.id, fileName: res.fileName, fileUrl: res.fileUrl, fileSize: res.fileSize });
|
||||
return false;
|
||||
}
|
||||
|
||||
async function save(publishAfter: boolean) {
|
||||
const payload: SvcItemSaveRequest = {
|
||||
...form.value,
|
||||
id: route.query.id ? Number(route.query.id) : undefined,
|
||||
attachmentIds: attachments.value.map(a => a.id),
|
||||
};
|
||||
const id: number = await svcItemApi.save(payload).send();
|
||||
if (publishAfter) {
|
||||
await svcItemApi.publish(currentSiteCode.value, id).send();
|
||||
Message.success("已保存并发布");
|
||||
} else {
|
||||
Message.success("已保存");
|
||||
}
|
||||
router.replace({ path: "/svc/item/edit", query: { id: String(id) } });
|
||||
}
|
||||
|
||||
/** 流程图:flow-center 深链整页跳转(事项库场景用科室视图) */
|
||||
function openFlowDiagram() {
|
||||
window.open(`${config.flowHost}/#/diagram?flowGuid=${form.value.flowGuid}&mode=dept`, "_blank");
|
||||
}
|
||||
|
||||
onMounted(loadAll);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<Card :bordered="false" dis-hover>
|
||||
<template #title>{{ pageTitle }}</template>
|
||||
<Form :label-width="130">
|
||||
<Divider orientation="left">基本信息</Divider>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="事项编码" required>
|
||||
<Input v-model="form.itemCode" placeholder="字母/数字/下划线/中划线,2~64 位" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="事项名称" required>
|
||||
<Input v-model="form.title" :maxlength="200" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="24">
|
||||
<FormItem label="一句话简介">
|
||||
<Input v-model="form.summary" type="textarea" :rows="2" :maxlength="500" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="服务对象">
|
||||
<Select v-model="form.objectKey" clearable>
|
||||
<Option v-for="d in objects" :key="d.dictKey" :value="d.dictKey">{{ d.dictName }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="事项类别">
|
||||
<Select v-model="form.categoryKey" clearable>
|
||||
<Option v-for="d in categories" :key="d.dictKey" :value="d.dictKey">{{ d.dictName }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="服务部门">
|
||||
<Select v-model="form.deptGroupId" clearable>
|
||||
<Option v-for="d in deptOptions" :key="d.id" :value="d.id">{{ d.label }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Divider orientation="left">办理信息</Divider>
|
||||
<Row :gutter="16">
|
||||
<Col span="8">
|
||||
<FormItem label="办理方式">
|
||||
<RadioGroup v-model="form.handleMode">
|
||||
<Radio :label="1">线上</Radio>
|
||||
<Radio :label="2">线下</Radio>
|
||||
<Radio :label="3">线上线下</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="可在线办理">
|
||||
<Switch v-model="form.onlineFlag" :true-value="1" :false-value="0" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="办理地点">
|
||||
<Input v-model="form.handlePlace" :maxlength="200" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="办理时限">
|
||||
<Input v-model="form.handleTimeLimit" :maxlength="64" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="承诺时限">
|
||||
<Input v-model="form.promiseTimeLimit" :maxlength="64" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="咨询电话">
|
||||
<Input v-model="form.contactPhone" :maxlength="64" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="监督投诉电话">
|
||||
<Input v-model="form.supervisePhone" :maxlength="64" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Divider orientation="left">在线办理绑定</Divider>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="办理流程">
|
||||
<div class="flow-select">
|
||||
<Select v-model="form.flowGuid" clearable :disabled="flowDisabled" placeholder="流程中心流程">
|
||||
<Option v-for="f in flows" :key="f.flowGuid" :value="f.flowGuid">
|
||||
{{ f.name }}(v{{ f.version }})
|
||||
</Option>
|
||||
</Select>
|
||||
<Button v-if="form.flowGuid" size="small" @click="openFlowDiagram">流程图</Button>
|
||||
</div>
|
||||
<div v-if="flowDisabled" class="tip">{{ flowDisabledReason || "流程中心未接入,暂只能配置线下事项" }}</div>
|
||||
<div v-else class="tip">可在线办理的事项必须绑定流程</div>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="办理表单">
|
||||
<Select v-model="form.formCode" clearable placeholder="该事项发起时填写的自定义表单">
|
||||
<Option v-for="f in forms" :key="f.code" :value="f.code">{{ f.name }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="24">
|
||||
<FormItem label="我要办理外链">
|
||||
<Input v-model="form.applyUrl" placeholder="http(s):// 完整地址;填写后优先于自动生成地址" />
|
||||
<div class="tip">优先级:本外链 > 办事大厅自动生成 > 不显示按钮</div>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Divider orientation="left">关联栏目与资产</Divider>
|
||||
<Row :gutter="16">
|
||||
<Col span="8">
|
||||
<FormItem label="常见问题栏目">
|
||||
<InputNumber v-model="form.faqCategoryId" :min="1" style="width: 100%" placeholder="CMS 栏目 id" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="公告信息栏目">
|
||||
<InputNumber v-model="form.noticeCategoryId" :min="1" style="width: 100%" placeholder="CMS 栏目 id" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<FormItem label="文件规定栏目">
|
||||
<InputNumber v-model="form.ruleCategoryId" :min="1" style="width: 100%" placeholder="CMS 栏目 id" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="事项封面">
|
||||
<div class="cover">
|
||||
<img v-if="form.coverImage" :src="form.coverImage" class="cover-img" />
|
||||
<Upload :before-upload="uploadCover" :show-upload-list="false" accept="image/*">
|
||||
<Button :loading="uploadingCover" icon="ios-image-outline">上传封面</Button>
|
||||
</Upload>
|
||||
<Button v-if="form.coverImage" size="small" type="text" @click="form.coverImage = ''">移除</Button>
|
||||
</div>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="6">
|
||||
<FormItem label="排序值">
|
||||
<InputNumber v-model="form.sortNo" style="width: 100%" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Divider orientation="left">指南内容</Divider>
|
||||
<FormItem label="办理流程">
|
||||
<RichTextEditor v-model="form.processHtml" placeholder="办理流程说明" />
|
||||
</FormItem>
|
||||
<FormItem label="所需材料">
|
||||
<RichTextEditor v-model="form.materialHtml" placeholder="所需材料说明" />
|
||||
</FormItem>
|
||||
<FormItem label="注意事项">
|
||||
<RichTextEditor v-model="form.noticeHtml" placeholder="注意事项" />
|
||||
</FormItem>
|
||||
<FormItem label="指南附件">
|
||||
<Upload :before-upload="uploadGuide" :show-upload-list="false">
|
||||
<Button icon="ios-cloud-upload-outline">上传附件</Button>
|
||||
</Upload>
|
||||
<div class="att-list">
|
||||
<div v-for="a in attachments" :key="a.id" class="att-item">
|
||||
<a :href="a.fileUrl" target="_blank" rel="noopener">{{ a.fileName }}</a>
|
||||
<Button size="small" type="text" style="color: #ed4014"
|
||||
@click="attachments = attachments.filter(x => x.id !== a.id)">移除</Button>
|
||||
</div>
|
||||
</div>
|
||||
</FormItem>
|
||||
|
||||
<div class="actions">
|
||||
<Button type="primary" :loading="false" @click="save(false)">保存</Button>
|
||||
<Button type="success" @click="save(true)">保存并发布</Button>
|
||||
<Button @click="router.back()">返回</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.flow-select {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.tip {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
.cover {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.cover-img {
|
||||
height: 48px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.att-list {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.att-item {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.actions {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user