| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- <template>
- <div class="about main-background main-background-type0">
- <div class="nav-placeholder" />
- <section class="main-section large">
- <div class="content">
- <div class="title left-right">
- <a-button :icon="h(ArrowLeftOutlined)" @click="router.back()">返回</a-button>
- <h2>传承协议签署</h2>
- <div class="w-20"></div>
- </div>
- <a-spin :spinning="loader.loading.value">
- <template v-if="!loader.loading.value">
- <a-result
- v-if="!currentAgreement"
- status="info"
- title="您还未签署传承协议"
- sub-title="请先签署传承协议"
- >
- <template #extra>
- <a-button type="primary" @click="createAgreement">去签署传承协议</a-button>
- </template>
- </a-result>
- <div v-else>
- <a-alert type="info" message="请仔细阅读传承协议,确保您已理解内容,并签署传承协议。" class="mb-4" show-icon />
- <a-form
- v-if="currentAgreement"
- ref="formRef"
- :model="currentAgreement"
- :rules="formRules"
- layout="vertical"
- >
- <a-typography-title class="mt-5!" :level="4">{{ agreementTitle }}</a-typography-title>
- <AgreementBodyNational
- v-if="currentAgreement?.level === 23"
- :detail="(currentAgreement as AgreementDetail)"
- :agreement-year="agreementYear"
- :party-a-stamp-date="partyAStampDate"
- :party-b-sign-date="partyBSignDate"
- @update:party-a-stamp-date="partyAStampDate = $event"
- @update:party-b-sign-date="partyBSignDate = $event"
- />
- <AgreementBodyProvincial
- v-else-if="currentAgreement?.level === 24"
- :detail="(currentAgreement as AgreementDetail)"
- :agreement-year="agreementYear"
- :party-a-stamp-date="partyAStampDate"
- :party-b-sign-date="partyBSignDate"
- @update:party-a-stamp-date="partyAStampDate = $event"
- @update:party-b-sign-date="partyBSignDate = $event"
- />
- <AgreementBodyMunicipal
- v-else-if="currentAgreement?.level === 25"
- :detail="(currentAgreement as AgreementDetail)"
- :agreement-year="agreementYear"
- :party-a-stamp-date="partyAStampDate"
- :party-b-sign-date="partyBSignDate"
- @update:party-a-stamp-date="partyAStampDate = $event"
- @update:party-b-sign-date="partyBSignDate = $event"
- />
- </a-form>
- <a-space direction="vertical" class="w-full mt-4" size="middle">
- <a-button type="primary" block :loading="submitLoading" @click="saveAgreement">保存传承协议</a-button>
- <a-button block :loading="submitLoading" @click="downloadAgreement">下载协议 PDF</a-button>
- </a-space>
- </div>
- </template>
- </a-spin>
- </div>
- </section>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, h, onMounted, ref, watch } from 'vue';
- import { useRoute, useRouter } from 'vue-router';
- import { message, Modal } from 'ant-design-vue';
- import type { FormInstance } from 'ant-design-vue';
- import type { Rules } from 'async-validator';
- import { RequestApiError } from '@imengyu/imengyu-utils';
- import { useAuthStore } from '@/stores/auth';
- import AssessmentContentApi, { AgreementDetail } from '@/api/collect/AssessmentContent';
- import AgreementBodyNational from './components/AgreementBodyNational.vue';
- import AgreementBodyProvincial from './components/AgreementBodyProvincial.vue';
- import AgreementBodyMunicipal from './components/AgreementBodyMunicipal.vue';
- import type { AgreementYmdParts } from './components/AgreementDateWriteBlock.vue';
- import { useSimpleDataLoader } from '@/composeables/useSimpleDataLoader';
- import { ArrowLeftOutlined } from '@ant-design/icons-vue';
- import { injectAppConfiguration } from '@/api/system/useAppConfiguration';
- function formatErr(e: unknown): string {
- if (e instanceof RequestApiError)
- return e.errorMessage;
- if (e instanceof Error)
- return e.message;
- return String(e);
- }
- const router = useRouter();
- const route = useRoute();
- const authStore = useAuthStore();
- const queryId = computed(() => Number(route.query.id) || 0);
- const queryUserId = computed(() => Number(route.query.userId) || 0);
- const currentAgreement = ref<AgreementDetail | null>(null);
- const partyAStampDate = ref<AgreementYmdParts>({ year: '', month: '', day: '' });
- const partyBSignDate = ref<AgreementYmdParts>({ year: '', month: '', day: '' });
- const formRef = ref<FormInstance | null>(null);
- const CN_MOBILE_RE = /^1\d{10}$/;
- const CN_ID_RE = /^(?:\d{15}|\d{17}[\dXx])$/;
- const formRules = computed<Rules>(() => {
- const rules: Rules = {
- partyB: [{ required: true, message: '请填写乙方(传承人)姓名' }],
- apprentice: [
- { required: true, message: '请填写本年度带徒人数' },
- { type: 'integer', min: 0, message: '须为不小于 0 的整数' },
- ],
- activity: [
- { required: true, message: '请填写本年度宣传活动场次' },
- { type: 'integer', min: 0, message: '须为不小于 0 的整数' },
- ],
- partyBSign: [
- {
- validator(_rule, value, callback) {
- const s = typeof value === 'string' ? value.trim() : '';
- if (!s)
- callback(new Error('请完成乙方签名'));
- else
- callback();
- },
- },
- ],
- idCard: [
- { required: true, message: '请填写身份证号' },
- {
- validator(_rule, value, callback) {
- const s = value != null ? String(value).trim() : '';
- if (!CN_ID_RE.test(s))
- callback(new Error('请输入正确的身份证号'));
- else
- callback();
- },
- },
- ],
- ich: [{ required: true, message: '请填写非遗项目名称' }],
- health: [{ required: true, message: '请填写身体状况' }],
- mobile: [
- { required: true, message: '请填写乙方联系电话' },
- {
- validator(_rule, value, callback) {
- const s = value != null ? String(value).trim() : '';
- if (!CN_MOBILE_RE.test(s))
- callback(new Error('请输入正确的手机号'));
- else
- callback();
- },
- },
- ],
- };
- if (currentAgreement.value?.level !== 25)
- rules.course = [
- { required: true, message: '请填写本年度研修班场次' },
- { type: 'integer', min: 0, message: '须为不小于 0 的整数' },
- ];
- return rules;
- });
- async function loadBasicInfo() {
- const uid = authStore.userInfo?.id ?? authStore.userId;
- const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);
- const d = currentAgreement.value;
- if (!d)
- return;
- d.partyB = basicInfo.name;
- d.mobile = basicInfo.mobile;
- d.idCard = basicInfo.idCard;
- d.ich = basicInfo.ichName;
- d.level = basicInfo.level;
- }
- const agreementYear = computed(() => currentAgreement.value?.year ?? 2027);
- const agreementTitle = computed(
- () => `${agreementYear.value} 年度${levelTitle.value}非物质文化遗产代表性传承人传承协议`,
- );
- const levelTitle = computed(() => {
- if (currentAgreement.value?.level === 23) return '国家级';
- if (currentAgreement.value?.level === 24) return '省级';
- return '市级';
- });
- const loader = useSimpleDataLoader(async () => {
- if (queryId.value > 0) {
- const detail = await AssessmentContentApi.getAgreementDetail(queryId.value, queryUserId.value || undefined);
- currentAgreement.value = detail;
- partyAStampDate.value = { year: '', month: '', day: '' };
- partyBSignDate.value = { year: '', month: '', day: '' };
- return currentAgreement.value;
- }
- const uid = authStore.userInfo?.id ?? authStore.userId;
- const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);
- if (basicInfo.agreementId > 0) {
- const detail = await AssessmentContentApi.getAgreementDetail(
- basicInfo.agreementId,
- uid,
- );
- currentAgreement.value = detail;
- partyAStampDate.value = { year: '', month: '', day: '' };
- partyBSignDate.value = {
- year: detail.updatetime.getFullYear().toString(),
- month: (detail.updatetime.getMonth() + 1).toString(),
- day: detail.updatetime.getDate().toString(),
- };
- } else {
- currentAgreement.value = null;
- }
- return currentAgreement.value;
- }, {
- immediate: false,
- });
- onMounted(() => {
- loader.load();
- });
- watch(
- () => [queryId.value, queryUserId.value],
- () => {
- loader.load();
- },
- );
- const submitLoading = ref(false);
- const appConfiguration = injectAppConfiguration();
- async function createAgreement() {
- const now = new Date();
- const u = authStore.userInfo;
- const nick = u?.nickname || u?.username || '';
- const uid = authStore.userInfo?.id ?? authStore.userId;
- const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);
- const detail = new AgreementDetail();
- detail.userId = uid;
- detail.year = appConfiguration.value?.collectSignYear || now.getFullYear() + 1;
- detail.level = basicInfo.level;
- if (basicInfo.level === 24) {
- detail.partyA = '厦门市文化和旅游局';
- } else if (basicInfo.level === 25) {
- detail.partyA = basicInfo.regionText + '文化和旅游局';
- } else {
- detail.partyA = '福建省文化和旅游厅';
- }
- detail.partyB = nick;
- partyAStampDate.value = {
- year: now.getFullYear().toString(),
- month: (now.getMonth() + 1).toString(),
- day: now.getDate().toString(),
- };
- partyBSignDate.value = {
- year: now.getFullYear().toString(),
- month: (now.getMonth() + 1).toString(),
- day: now.getDate().toString(),
- };
- currentAgreement.value = detail;
- await loadBasicInfo();
- }
- async function saveAgreement() {
- try {
- await formRef.value?.validate();
- } catch {
- message.warning('请填写完整信息');
- return;
- }
- submitLoading.value = true;
- try {
- const d = currentAgreement.value;
- if (!d) {
- submitLoading.value = false;
- return;
- }
- await AssessmentContentApi.saveAgreement(d as AgreementDetail);
- message.success('保存传承协议成功');
- } catch (error) {
- Modal.error({
- title: '保存传承协议失败',
- content: formatErr(error),
- });
- }
- submitLoading.value = false;
- }
- async function downloadAgreement() {
- if (!currentAgreement.value?.id) {
- message.warning('请先保存传承协议后再下载 PDF');
- return;
- }
- try {
- await AssessmentContentApi.downloadAgreementPdf(currentAgreement.value.id);
- message.success('已开始下载');
- } catch (error) {
- Modal.error({
- title: '下载失败',
- content: formatErr(error),
- });
- }
- }
- </script>
|