index.vue 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <template>
  2. <div class="p-3">
  3. <div class="bg-fff flex1 ov-hd d-flex flex-cln">
  4. <div class="d-flex a-c pd-16 border-bottom">
  5. <div class="f-s-20 c-333 f-w-7 mr-10">{{ form.id ? '编辑' : '新增' }}会议</div>
  6. <el-button @click="router.go(-1)" type="primary" text>
  7. <el-icon>
  8. <Back />
  9. </el-icon>
  10. 返回上一级
  11. </el-button>
  12. </div>
  13. <div class="flex1 over-auto">
  14. <el-form ref="formRef" label-width="auto" label-position="top" :model="form" :rules="rules" :scroll-into-view-options="scrollOptions" scroll-to-error>
  15. <div class="pd-16 border-bottom ov-hd">
  16. <div class="info-title mb-10">会议信息</div>
  17. <el-row :gutter="20">
  18. <el-col :span="6">
  19. <el-form-item label="会议名称" prop="trainingName">
  20. <el-input v-model="form.trainingName" placeholder="请输入会议名称" clearable />
  21. </el-form-item>
  22. </el-col>
  23. <el-col :span="6">
  24. <el-form-item label="会议时间" prop="trainingTime">
  25. <el-date-picker v-model="form.trainingTime" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" date-format="YYYY-MM-DD" time-format="HH:mm" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
  26. </el-form-item>
  27. </el-col>
  28. <el-col :span="6">
  29. <el-form-item label="报名时间" prop="signupTime">
  30. <el-date-picker v-model="form.signupsTime" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" date-format="YYYY-MM-DD" time-format="HH:mm" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
  31. </el-form-item>
  32. </el-col>
  33. <el-col :span="6">
  34. <el-form-item label="会议方式" prop="joinType">
  35. <!-- 单选框 -->
  36. <el-radio-group v-model="form.joinType">
  37. <el-radio v-for="item in lm_training_join_type" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
  38. </el-radio-group>
  39. </el-form-item>
  40. </el-col>
  41. <el-col v-if="form.joinType === '0'" :span="6">
  42. <el-form-item label="会议地点" prop="trainingLocation">
  43. <el-input v-model="form.trainingLocation" placeholder="请输入会议地点" clearable />
  44. </el-form-item>
  45. </el-col>
  46. <el-col :span="6">
  47. <el-form-item label="会议联系人" prop="contactName">
  48. <el-input v-model="form.contactName" maxlength="40" placeholder="请输入会议联系人" clearable />
  49. </el-form-item>
  50. </el-col>
  51. <el-col :span="6">
  52. <el-form-item label="联系电话" prop="tel">
  53. <el-input v-model="form.tel" maxlength="20" placeholder="请输入联系电话" clearable />
  54. </el-form-item>
  55. </el-col>
  56. <el-col :span="12">
  57. <el-form-item label="可报名人员类型" prop="conditions.typeCheck">
  58. <el-checkbox-group v-model="checkedVipLevels" @change="handleCheckedChange">
  59. <el-checkbox v-for="city in form.conditions.typeCheck" :key="city" :label="city" :value="city">
  60. {{ selectDictLabels(dm_check_join_type, city.vipLevel, ',') }}
  61. </el-checkbox>
  62. </el-checkbox-group>
  63. <div @click="handleCheckAllChange(true)" v-if="!checkAll" class="pl-10 c-s-p">
  64. <u>全选</u>
  65. </div>
  66. <div @click="handleCheckAllChange(false)" v-else class="pl-10 c-s-p"><u>取消全选</u></div>
  67. </el-form-item>
  68. </el-col>
  69. <el-col :span="6">
  70. <el-form-item label="报名人数" prop="conditions.totalCheck">
  71. <div class="d-flex a-c">
  72. <el-radio-group v-model="form.conditions.totalCheck" style="flex-wrap: nowrap">
  73. <el-radio label="0">不限制</el-radio>
  74. <el-radio label="1">限制</el-radio>
  75. </el-radio-group>
  76. </div>
  77. </el-form-item>
  78. </el-col>
  79. <el-col :span="12" v-if="form.conditions.totalCheck == '1'">
  80. <el-form-item prop="restrictiveConditions">
  81. <template #label>
  82. <span>限制条件</span>
  83. <span class="c-999 f-s-12 f-w-4">(不限制的条件可不填)</span>
  84. </template>
  85. <div class="d-flex flex-cln pl-10">
  86. <div class="d-flex">
  87. <div class="c-#606266 f-w-6" style="">报名总人数限制:</div>
  88. <el-input class="flex1 pl-5" v-model="form.conditions.total" maxlength="20" placeholder="请输入报名人数" clearable :disabled="form.conditions.totalCheck !== '1'" style="max-width: 200px;" />
  89. </div>
  90. <div class="c-#606266 f-w-6" style="">条件限制:</div>
  91. <div class="d-flex">
  92. <div class="d-flex flex-cln">
  93. <el-checkbox v-model="form.conditions.levelTotalCheck" label="按单位类型限制" size="large" true-value="1" false-value="0" />
  94. <div v-if="form.conditions.levelTotalCheck == '1' && form.conditions.cpyTotalCheck == '0'" class="pl-10 pr-10 pt-5 pb-5 border">
  95. <template v-for="(item, index) in form.conditions.levelCheck" :key="index">
  96. <div class="d-flex a-c" v-if="checkedVipLevels.some(items => items.vipLevel === item.vipLevel)">
  97. <el-checkbox v-model="item.check" true-value="1" false-value="0" :label="'所有' + selectDictLabels(dm_check_join_type, item.vipLevel, ',') + '参会人数≤'" size="large" />
  98. <el-input class="pl-10" v-model="item.total" maxlength="20" placeholder="请输入报名人数" :disabled="!+item.check" />
  99. </div>
  100. </template>
  101. <el-empty :image-size="20" description="请先选择报名人员类型" v-if="checkedVipLevels.length == 0" />
  102. </div>
  103. </div>
  104. <div class="d-flex flex-cln ml-20">
  105. <el-checkbox v-model="form.conditions.cpyTotalCheck" label="按每家单位人数限制" size="large" true-value="1" false-value="0" />
  106. <div v-if="form.conditions.cpyTotalCheck == '1' && form.conditions.levelTotalCheck == '0'" class="pl-10 pr-10 pt-5 pb-5 border">
  107. <template v-for="(item, index) in form.conditions.cpyCheck" :key="index">
  108. <div class="d-flex a-c" v-if="checkedVipLevels.some(items => items.vipLevel === item.vipLevel)">
  109. <el-checkbox v-model="item.check" :label="selectDictLabels(dm_check_join_type, item.vipLevel, ',') + '限制每家单位人数≤'" size="large" true-value="1" false-value="0" />
  110. <el-input class="pl-10" v-model="item.total" maxlength="20" placeholder="请输入报名人数" :disabled="!+item.check" />
  111. </div>
  112. </template>
  113. <el-empty :image-size="20" description="请先选择报名人员类型" v-if="checkedVipLevels.length == 0" />
  114. </div>
  115. </div>
  116. </div>
  117. <div class="d-flex border" v-if="form.conditions.levelTotalCheck == '1' && form.conditions.cpyTotalCheck == '1'">
  118. <div class="pl-10 pr-10 pt-5 pb-5 ">
  119. <template v-for="(item, index) in form.conditions.levelCheck" :key="index">
  120. <div class="d-flex a-c" v-if="checkedVipLevels.some(items => items.vipLevel === item.vipLevel)">
  121. <el-checkbox v-model="item.check" :label="'所有' + selectDictLabels(dm_check_join_type, item.vipLevel, ',') + '参会人数≤'" size="large" true-value="1" false-value="0" />
  122. <el-input class="pl-10" v-model="item.total" maxlength="20" placeholder="请输入报名人数" :disabled="!+item.check" />
  123. </div>
  124. </template>
  125. </div>
  126. <div class="pl-10 pr-10 pt-5 pb-5">
  127. <template v-for="(item, index) in form.conditions.cpyCheck" :key="index">
  128. <div class="d-flex a-c" v-if="checkedVipLevels.some(items => items.vipLevel === item.vipLevel)">
  129. <el-checkbox v-model="item.check" :label="selectDictLabels(dm_check_join_type, item.vipLevel, ',') + '限制每家单位人数≤'" size="large" true-value="1" false-value="0" />
  130. <el-input class="pl-10" v-model="item.total" maxlength="20" placeholder="请输入报名人数" :disabled="!+item.check" />
  131. </div>
  132. </template>
  133. </div>
  134. <div class="flex1 ml--10" v-if="checkedVipLevels.length == 0"><el-empty :image-size="20" description="请先选择报名人员类型" /></div>
  135. </div>
  136. </div>
  137. </el-form-item>
  138. </el-col>
  139. <el-col :span="6">
  140. <div class="d-flex flex-cln j-st" style="">
  141. <el-form-item label="发放积分" prop="pointsFlag" class="">
  142. <el-radio-group v-model="form.pointsFlag" style="flex-wrap: nowrap">
  143. <el-radio label="1">是</el-radio>
  144. <el-radio label="0">否</el-radio>
  145. </el-radio-group>
  146. </el-form-item>
  147. <el-form-item label="" prop="points" v-if="form.pointsFlag == '1'" class="flex1">
  148. <div class="d-flex f-s-14" style="white-space: nowrap;">
  149. <div>每成功参会(签到成功)1人发放</div>
  150. <el-input v-model="form.points" style="width: 45px" />
  151. <div>个单位积分。</div>
  152. </div>
  153. </el-form-item>
  154. </div>
  155. </el-col>
  156. <el-col :span="12">
  157. <el-form-item label="是否收取参会费用" prop="meetingCharge.hasFee">
  158. <div class="d-flex a-c">
  159. <el-radio-group v-model="form.meetingCharge.hasFee" style="flex-wrap: nowrap">
  160. <el-radio label="0">否</el-radio>
  161. <el-radio label="1">是</el-radio>
  162. </el-radio-group>
  163. </div>
  164. </el-form-item>
  165. <el-form-item prop="meetingCharge.pricing" v-if="form.meetingCharge.hasFee == '1'">
  166. <div class="d-flex">
  167. <div class="c-#606266 f-w-6" style="">收费标准:</div>
  168. <el-input class="flex1 pl-5" v-model="form.meetingCharge.pricing" maxlength="20" placeholder="请输入收费标准" clearable style="max-width: 200px;" />
  169. <div class="pl-10">元/人</div>
  170. </div>
  171. </el-form-item>
  172. <el-form-item v-if="form.meetingCharge.hasFee == '1'" prop="meetingCharge.hasFlatFee">
  173. <div>
  174. <el-radio-group v-model="form.meetingCharge.hasFlatFee" style="display: flex;flex-direction: column;align-items: flex-start;">
  175. <el-radio label="0">所有人统一收取标准费用</el-radio>
  176. <el-radio label="1">
  177. 按报名人员类型收取,不同人员收取不同费用
  178. <span class="c-999">(不作设置默认统一收取标准费用。)</span>
  179. </el-radio>
  180. </el-radio-group>
  181. </div>
  182. </el-form-item>
  183. <div class="d-flex flex-cln" v-if="form?.meetingCharge?.hasFee == '1'&& form?.meetingCharge?.hasFlatFee =='1'">
  184. <div class="pl-10 pr-10 pt-5 pb-5 border">
  185. <template v-for="(item, index) in form.meetingCharge.typeCharge" :key="index">
  186. <div class="d-flex a-c" v-if="checkedVipLevels.some(items => items.vipLevel === item.vipLevel)">
  187. <el-checkbox v-model="item.check" true-value="1" false-value="0" :label="selectDictLabels(dm_check_join_type, item.vipLevel, ',') + '每个单位参会人员'" size="large" />
  188. <el-select v-model="item.certType" placeholder="" clearable style="width: 100px">
  189. <el-option v-for="item in hasPartialFree" :key="item.value" :label="item.label" :value="item.value" />
  190. </el-select>
  191. <div v-if="+item?.certType" class="d-flex a-c">
  192. <div v-if="item.vipLevel != 'P'" class="pl-10 f-s-14" style="white-space: nowrap;">每个单位免费</div>
  193. <div v-else class="pl-10 f-s-14" style="white-space: nowrap;">免费</div>
  194. <el-input class="pl-10" v-model="item.total" maxlength="20" placeholder="请输入免费人数" style="width: 130px" :disabled="!+item.check" />
  195. <div class="f-s-14" style="white-space: nowrap;">人,其余每人收费</div>
  196. <el-input class="pl-10" v-model="item.cost" maxlength="20" placeholder="请输入费用" style="width: 130px" :disabled="!+item.check" />
  197. <div f-s-14>元</div>
  198. </div>
  199. </div>
  200. </template>
  201. <el-empty :image-size="20" description="请先选择报名人员类型" v-if="checkedVipLevels.length == 0" />
  202. </div>
  203. </div>
  204. </el-col>
  205. <el-col :span="6">
  206. <el-form-item label="是否电子手签" prop="eleSignature">
  207. <div class="d-flex a-c">
  208. <el-radio-group v-model="form.eleSignature" style="flex-wrap: nowrap">
  209. <el-radio label="1">是</el-radio>
  210. <el-radio label="0">否</el-radio>
  211. </el-radio-group>
  212. </div>
  213. </el-form-item>
  214. </el-col>
  215. </el-row>
  216. <el-row :gutter="20">
  217. <el-col :span="12">
  218. <el-form-item label="会议详情" prop="description">
  219. <el-input v-model="form.description" :rows="4" type="textarea" placeholder="请输入会议详情" />
  220. </el-form-item>
  221. </el-col>
  222. <el-col :span="12">
  223. <el-form-item label="与会须知" prop="description">
  224. <el-input v-model="form.notice" :rows="4" type="textarea" placeholder="请输入与会须知" />
  225. </el-form-item>
  226. </el-col>
  227. <el-col :span="12">
  228. <el-form-item label="会议微信群聊二维码" prop="wechatQrCode">
  229. <ImageUpload v-model="form.wechatQrCode" :fileSize="20"></ImageUpload>
  230. </el-form-item>
  231. </el-col>
  232. <el-col :span="12">
  233. <el-form-item label="封面图" prop="coverImg">
  234. <ImageUpload v-model="form.coverImg" :limit="1" :fileSize="20"></ImageUpload>
  235. </el-form-item>
  236. </el-col>
  237. <el-col :span="12">
  238. <el-form-item label="会议图" prop="trainingImg">
  239. <ImageUpload v-model="form.trainingImg" :fileSize="20"></ImageUpload>
  240. </el-form-item>
  241. </el-col>
  242. <el-col :span="12">
  243. <el-form-item label="会议备注" prop="remark">
  244. <el-input v-model="form.remark" :rows="4" type="textarea" placeholder="请输入会议备注" />
  245. </el-form-item>
  246. </el-col>
  247. <el-col :span="12">
  248. <el-form-item label="相关文件" prop="attachments">
  249. <template #label>
  250. <span>相关文件</span>
  251. <span class="c-999 f-s-12 f-w-4">(此模块报名审核通过后才可查看)</span>
  252. </template>
  253. <div class="d-flex flex-cln mt-10 f-s-12 c-#606266">
  254. <FileUpload v-model="form.attachments" format="array" :limit="20" :fileSize="100"></FileUpload>
  255. </div>
  256. </el-form-item>
  257. </el-col>
  258. </el-row>
  259. </div>
  260. <div class="pd-16 border-bottom d-flex">
  261. <div class="w-50% ov-at h-1000">
  262. <div class="info-title mb-10">证书信息</div>
  263. <div class="d-flex j-start a-c">
  264. <el-form-item label="是否颁发证书" prop="certFlag">
  265. <el-radio-group v-model="form.certFlag">
  266. <el-radio v-for="item in yes_no" :key="item.value" :label="item.value">
  267. {{ item.label }}
  268. </el-radio>
  269. </el-radio-group>
  270. </el-form-item>
  271. <div v-if="+form.certFlag" class="ml-20">
  272. <el-button @click="addCertInfo" type="primary">新增证书</el-button>
  273. </div>
  274. </div>
  275. <template v-if="+form.certFlag">
  276. <template v-for="(item, index) in form.certificateInfo" :key="index">
  277. <el-row :gutter="20" class="bg-#f4f4f4 pd-16 mb-10">
  278. <el-col :span="10">
  279. <el-form-item label="证书名称" :prop="`certificateInfo.${index}.certType`" :rules="[{ required: true, message: '请选择证书名称', trigger: 'change' }]">
  280. <el-select v-model="item.certType" placeholder="证书名称" clearable>
  281. <el-option v-for="item in lm_training_cert" :key="item.value" :label="item.label" :value="item.value" />
  282. </el-select>
  283. </el-form-item>
  284. </el-col>
  285. <el-col :span="10">
  286. <el-form-item :prop="`certificateInfo.${index}.certImg`" :rules="[{ required: true, message: '请上传证书图片', trigger: 'change' }]">
  287. <template #label>
  288. <span>证书模板图片</span>
  289. <el-button @click="goEditor()" type="primary" text>去编辑模板图片</el-button>
  290. </template>
  291. <ImageUpload v-model="item.certImg" :fileSize="40" :limit="1"></ImageUpload>
  292. </el-form-item>
  293. </el-col>
  294. <el-col :span="4">
  295. <el-form-item>
  296. <el-button type="danger" @click="deleteItem(index)">删除</el-button>
  297. </el-form-item>
  298. </el-col>
  299. </el-row>
  300. </template>
  301. </template>
  302. </div>
  303. <div class="w-50% d-flex flex-cln j-c a-c pl-20">
  304. <el-button type="primary" class="w-100%" plain style="height: 70px; margin-bottom: 20px;" @click="showSignIn = true">点击去编辑报名信息></el-button>
  305. <div class="w-400 h-700 border over-auto">
  306. <div class="pd-10 border bg-#fafafa">
  307. <div class="pt-10 f-s-20 f-w-6 d-flex j-c a-c flex-cln ">报名信息</div>
  308. <div class="f-s-12 f-w-4 d-flex j-start c-red">此页面为意向人报名时所见页面:</div>
  309. </div>
  310. <template v-for="(item, index) in fixedField" :key="index">
  311. <div class="pd-15 border1 c-#D7D7D7 d-flex j-sb">
  312. <span class="c-#606266 f-s-16 f-w-6">{{ item.label }}</span>
  313. <span class="f-s-14">请输入</span>
  314. </div>
  315. </template>
  316. <template v-for="(field, index) in fields" :key="index">
  317. <div class="meeting-custom-wrapper" @click.stop="showSignIn = true">
  318. <meetingCustomPreview :field="field" style="pointer-events: none" v-if="field" />
  319. </div>
  320. </template>
  321. <div class="pd-15 border1 c-#D7D7D7 d-flex j-sb">
  322. <span class="c-#606266 f-s-16 f-w-6">备注</span>
  323. <span class="f-s-14">请输入</span>
  324. </div>
  325. </div>
  326. </div>
  327. </div>
  328. </el-form>
  329. <MeetingEditors v-if="showSignIn" v-model:show="showSignIn" v-model:info="fields" />
  330. </div>
  331. <div class="d-flex a-c j-c pd-16">
  332. <el-button @click="Cancel">取消</el-button>
  333. <el-button @click="save" type="primary">提交</el-button>
  334. </div>
  335. </div>
  336. </div>
  337. </template>
  338. <script setup name="meeting-add" lang="ts">
  339. import { trainingAdd, trainingDetail, trainingUpdate } from '@/api/training';
  340. import { debounce } from 'lodash';
  341. import { onMounted, reactive, ref } from 'vue';
  342. import { useRouter } from 'vue-router';
  343. import { FieldDefinition } from '../models/type';
  344. // 需要添加以下导入
  345. import meetingCustomPreview from '../models/meeting-custom-preview.vue';
  346. import MeetingEditors from '../models/meeting-editors.vue';
  347. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  348. const { lm_training_join_type, yes_no, lm_training_cert, dm_check_join_type } = toRefs<any>(proxy?.useDict('lm_training_join_type', 'yes_no', 'lm_training_cert', 'dm_check_join_type'));
  349. const fields = ref<FieldDefinition[]>([])
  350. const showSignIn = ref(false);
  351. const fixedField = ref<FieldDefinition[]>([{
  352. name: `ent-${generateSecureRandomString()}`,
  353. label: '企业名称', type: '1',
  354. required: '1', readonly: '0',
  355. }, {
  356. name: `name-${generateSecureRandomString()}`,
  357. label: '姓名', type: '1', readonly: '0',
  358. required: '1'
  359. }, {
  360. name: `pos-${generateSecureRandomString()}`,
  361. label: '职务', type: '1', readonly: '0',
  362. required: '1'
  363. }, {
  364. name: `Con-${generateSecureRandomString()}`,
  365. label: '联系方式', type: '1', readonly: '0',
  366. required: '1'
  367. }])
  368. const hasPartialFree = ref([{
  369. label: '全部免费',
  370. value: '0'
  371. }, {
  372. label: '部分免费',
  373. value: '1'
  374. }])
  375. const scrollOptions = {
  376. block: 'center',
  377. behavior: 'smooth'
  378. };
  379. function generateSecureRandomString(length = 8) {
  380. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  381. const randomValues = new Uint32Array(length);
  382. window.crypto.getRandomValues(randomValues);
  383. let result = '';
  384. randomValues.forEach((value) => {
  385. result += chars[value % chars.length];
  386. });
  387. return result;
  388. }
  389. const Cancel = () => {
  390. router.go(-1)
  391. }
  392. const router = useRouter();
  393. const route = useRoute();
  394. const levelTypeCheck = computed(() =>
  395. String(Number(checkedVipLevels.value.length > 0))
  396. );
  397. const form = ref<any>({
  398. id: undefined,
  399. conditions: {
  400. levelTotalCheck: '0',
  401. cpyTotalCheck: '0',
  402. levelTypeCheck: levelTypeCheck,
  403. typeCheck: [
  404. {
  405. vipLevel: "0",
  406. check: "0"
  407. },
  408. {
  409. vipLevel: "1",
  410. check: "0"
  411. },
  412. {
  413. vipLevel: "3",
  414. check: "0"
  415. },
  416. {
  417. vipLevel: "4",
  418. check: "0"
  419. },
  420. {
  421. vipLevel: "5",
  422. check: "0"
  423. },
  424. {
  425. vipLevel: "P",
  426. check: "0"
  427. }
  428. ],
  429. levelCheck: [{
  430. vipLevel: '0',
  431. check: "0",
  432. total: ''
  433. }, {
  434. vipLevel: '1',
  435. check: "0",
  436. total: ''
  437. }, {
  438. vipLevel: '3',
  439. check: "0",
  440. total: ''
  441. }, {
  442. vipLevel: '4',
  443. check: "0",
  444. total: ''
  445. }, {
  446. vipLevel: '5',
  447. check: "0",
  448. total: ''
  449. },
  450. {
  451. vipLevel: "P",
  452. check: "0"
  453. }],
  454. cpyCheck: [{
  455. vipLevel: '0',
  456. check: "0",
  457. total: ''
  458. }, {
  459. vipLevel: '1',
  460. check: "0",
  461. total: ''
  462. }, {
  463. vipLevel: '3',
  464. check: "0",
  465. total: ''
  466. }, {
  467. vipLevel: '4',
  468. check: "0",
  469. total: ''
  470. }, {
  471. vipLevel: '5',
  472. check: "0",
  473. total: ''
  474. }]
  475. },
  476. // 收取参会费用
  477. meetingCharge: {
  478. hasFee: null,//是否收取参会费用
  479. pricing: null,//收费标准
  480. hasFlatFee: null,//收费标准类型 0所有人统一收取费用 1按报名人员类型收取
  481. typeCharge: [{
  482. vipLevel: '0',
  483. check: "0",
  484. total: '',//免费的人数
  485. certType: '1',//全部免费还是部分免费 0全部 1部分
  486. cost: null //每人收费多少
  487. }, {
  488. vipLevel: '1',
  489. check: "0",
  490. total: '',
  491. certType: '1',
  492. cost: null
  493. }, {
  494. vipLevel: '3',
  495. check: "0",
  496. total: '',
  497. certType: '1',
  498. cost: null
  499. }, {
  500. vipLevel: '4',
  501. check: "0",
  502. total: '',
  503. certType: '1',
  504. cost: null
  505. }, {
  506. vipLevel: '5',
  507. check: "0",
  508. total: '',
  509. certType: '1',
  510. cost: null
  511. },
  512. {
  513. vipLevel: "P",
  514. check: "0",
  515. total: '',
  516. certType: '1',
  517. cost: null
  518. }],
  519. }
  520. });
  521. const checkAll = ref(false)
  522. const checkedVipLevels = ref([])
  523. // 选项变化时的处理
  524. const handleCheckedChange = (selectedValues) => {
  525. const selectedLevelMap = new Map(selectedValues.map(item => [item.vipLevel, true]));
  526. form.value.conditions.typeCheck.forEach(item => {
  527. item.check = selectedLevelMap.has(item.vipLevel) ? '1' : '0';
  528. });
  529. }
  530. // 全选/取消全选
  531. const handleCheckAllChange = (val: boolean) => {
  532. checkedVipLevels.value = val
  533. ? form.value.conditions.typeCheck.map(item => item)
  534. : []
  535. handleCheckedChange(checkedVipLevels.value)
  536. checkAll.value = val
  537. }
  538. const rules = reactive({
  539. // 自动生成全部
  540. trainingName: [{ required: true, message: '请输入会议名称', trigger: 'blur' }],
  541. trainingTime: [{ required: true, message: '请选择会议时间', trigger: 'blur' }],
  542. joinType: [{ required: true, message: '请选择会议方式', trigger: 'change' }],
  543. trainingLocation: [{ required: true, message: '请输入会议地点', trigger: 'blur' }],
  544. 'conditions.totalCheck': [{ required: true, message: '请选择是否限制报名人数', trigger: 'change' }],
  545. 'conditions.typeCheck': [
  546. {
  547. validator: (rule, value, callback) => {
  548. setTimeout(() => {
  549. const isChecked = value?.some(item => item.check == "1");
  550. if (!isChecked) {
  551. callback(new Error('请至少选择一种可报名单位类型'));
  552. } else {
  553. callback();
  554. }
  555. }, 300);
  556. },
  557. required: true,
  558. trigger: 'change' // 触发校验的时机
  559. }
  560. ],
  561. 'meetingCharge.hasFee': [{ required: true, message: '请选择是否收取参会费用', trigger: 'change' }],
  562. 'meetingCharge.pricing': [{ required: true, message: '请输入收费标准', trigger: 'blur' }],
  563. 'meetingCharge.hasFlatFee': [{ required: true, message: '请选择收费标准', trigger: 'change' }],
  564. certFlag: [{ required: true, message: '请选择是否颁发证书', trigger: 'change' }],
  565. certificateInfo: [{ required: true, message: '请选择证书名称', trigger: 'change' }],
  566. description: [{ required: true, message: '请输入会议详情', trigger: 'blur' }],
  567. eleSignature: [{ required: true, message: '请选择是否电子手签', trigger: 'blur' }],
  568. pointsFlag: [{ required: true, message: '请选择是否发放积分', trigger: 'blur' }],
  569. // coverImg: [{ required: true, message: '请上传封面图', trigger: 'change' }],
  570. // trainingImg: [{ required: true, message: '请上传会议图', trigger: 'change' }],
  571. contactName: [{ required: true, message: '请输入会议联系人', trigger: 'blur' }],
  572. tel: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
  573. });
  574. const formRef = ref();
  575. const save = debounce(async () => {
  576. await formRef.value.validate();
  577. form?.value?.conditions?.typeCheck?.forEach(typeItem => {
  578. if (typeItem.check === "0") {
  579. // Update cpyCheck
  580. const cpyItem = form.value?.conditions?.cpyCheck?.find(item => item.vipLevel === typeItem.vipLevel);
  581. if (cpyItem) {
  582. cpyItem.check = "0";
  583. }
  584. // Update levelCheck
  585. const levelItem = form.value?.conditions?.levelCheck?.find(item => item.vipLevel === typeItem.vipLevel);
  586. if (levelItem) {
  587. levelItem.check = "0";
  588. }
  589. }
  590. });
  591. const params = {
  592. ...form.value,
  593. trainingStart: form.value?.trainingTime ? form.value.trainingTime[0] : undefined,
  594. trainingEnd: form.value?.trainingTime ? form.value.trainingTime[1] : undefined,
  595. signupStart: form.value?.signupsTime ? form.value.signupsTime[0] : undefined,
  596. signupEnd: form.value?.signupsTime ? form.value.signupsTime[1] : undefined,
  597. certificateInfo: +form.value?.certFlag ? form.value.certificateInfo : undefined,
  598. questions: fields.value.map((item, index) => ({
  599. ...item,
  600. sort: index + 1 // 从 1 开始
  601. }))
  602. };
  603. const res = form.value.id ? await trainingUpdate(params) : await trainingAdd(params);
  604. if (res && res.code === 200) {
  605. router.go(-1);
  606. }
  607. }, 500);
  608. const goEditor = () => {
  609. window.open('https://lm.yujin.shuziyunyao.com/poster#/editor', '_blank');
  610. }
  611. const addCertInfo = () => {
  612. if (!form.value.certificateInfo) {
  613. form.value.certificateInfo = [];
  614. }
  615. form.value.certificateInfo.push({
  616. certName: undefined,
  617. certImg: undefined
  618. });
  619. };
  620. const deleteItem = (index: number) => {
  621. form.value.certificateInfo.splice(index, 1);
  622. };
  623. // 获取专家详情
  624. const getMeetingDetail = async () => {
  625. if (route.query?.id) {
  626. const res = await trainingDetail(route.query?.id as string);
  627. if (!res || res.code !== 200) return;
  628. form.value = {
  629. ...res.data,
  630. trainingTime: res.data?.trainingStart && res.data?.trainingEnd ? [res.data.trainingStart, res.data.trainingEnd] : undefined,
  631. signupsTime: res.data?.signupStart && res.data?.signupEnd ? [res.data.signupStart, res.data.signupEnd] : undefined,
  632. conditions: (res.data?.conditions?.typeCheck == null) ? form.value.conditions : (res.data?.conditions || form.value.conditions),
  633. meetingCharge: res.data?.meetingCharge || form.value.meetingCharge
  634. };
  635. fields.value = res.data.questions
  636. if(form.value?.meetingCharge.pricing && typeof form.value?.meetingCharge.pricing === 'string'){
  637. form.value.meetingCharge.pricing = Number(form.value?.meetingCharge.pricing)
  638. }
  639. form.value?.conditions?.typeCheck?.forEach((i) => {
  640. if (i.check == '1') {
  641. checkedVipLevels.value.push(i)
  642. }
  643. })
  644. }
  645. };
  646. onMounted(() => {
  647. getMeetingDetail();
  648. });
  649. </script>
  650. <style scoped>
  651. .border {
  652. border: 1px solid #dcdfe6;
  653. }
  654. </style>