Config-edit.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <template>
  2. <el-dialog v-model="state.dialogVisible" width="70%" draggable title="编辑随手拍信息配置" @close="close">
  3. <el-form :model="state.ruleForm" label-width="110px" ref="ruleFormRef" :disabled="loading" scroll-to-error>
  4. <el-row :gutter="10">
  5. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  6. <el-form-item label="类型" prop="bulletinObj" :rules="[{ required: true, message: '请选择类型', trigger: 'change' }]">
  7. <el-select
  8. v-model="state.ruleForm.bulletinObj"
  9. placeholder="请选择类型"
  10. value-key="dicDataValue"
  11. class="w100"
  12. @change="
  13. (e:any) => {
  14. state.ruleForm.bulletinTypeId = e.dicDataValue;
  15. state.ruleForm.bulletinTypeName = e.dicDataName;
  16. }
  17. "
  18. >
  19. <el-option v-for="item in bulletinTypeOptions" :value="item" :key="item.dicDataValue" :label="item.dicDataName" />
  20. </el-select>
  21. </el-form-item>
  22. </el-col>
  23. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  24. <el-form-item label="来源" prop="sourceMode" :rules="[{ required: true, message: '请选择来源', trigger: 'change' }]">
  25. <el-select v-model="state.ruleForm.sourceMode" placeholder="请选择来源" value-key="dicDataValue" class="w100">
  26. <el-option v-for="item in bulletinSource" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
  27. </el-select>
  28. </el-form-item>
  29. </el-col>
  30. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  31. <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请填写标题', trigger: 'blur' }]">
  32. <el-input v-model="state.ruleForm.title" placeholder="请填写标题" clearable show-word-limit maxlength="200"></el-input>
  33. </el-form-item>
  34. </el-col>
  35. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  36. <el-form-item label="文档编码" prop="no" :rules="[{ required: false, message: '请填写文档编码', trigger: 'blur' }]">
  37. <el-input v-model="state.ruleForm.no" placeholder="请填写文档编码" clearable></el-input>
  38. </el-form-item>
  39. </el-col>
  40. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  41. <el-form-item label="时间" prop="bulletinTime" :rules="[{ required: false, message: '请选择时间', trigger: 'change' }]">
  42. <el-date-picker
  43. v-model="state.ruleForm.bulletinTime"
  44. type="datetime"
  45. placeholder="请选择时间"
  46. value-format="YYYY-MM-DD[T]HH:mm:ss"
  47. class="w100"
  48. :disabled-date="disabledDate"
  49. popper-class="no-atTheMoment"
  50. />
  51. </el-form-item>
  52. </el-col>
  53. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  54. <el-form-item label="所属部门" prop="departmentId" :rules="[{ required: true, message: '请选择所属部门', trigger: 'change' }]">
  55. <el-cascader
  56. :options="orgsOptions"
  57. filterable
  58. :props="{ value: 'id', label: 'name', emitPath: false, checkStrictly: true }"
  59. placeholder="请选择所属部门"
  60. class="w100"
  61. v-model="state.ruleForm.departmentId"
  62. ref="orgRef"
  63. @change="changeOrg"
  64. clearable
  65. >
  66. </el-cascader>
  67. </el-form-item>
  68. </el-col>
  69. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  70. <el-form-item label="来源部门" prop="sourceOrgName" :rules="[{ required: false, message: '请填写来源部门', trigger: 'change' }]">
  71. <el-input v-model="state.ruleForm.sourceOrgName" placeholder="请填写来源部门" clearable></el-input>
  72. </el-form-item>
  73. </el-col>
  74. <el-col>
  75. <el-form-item label="公告标识">
  76. <el-checkbox v-model="state.ruleForm.isOpen">是否公开</el-checkbox>
  77. <el-checkbox v-model="state.ruleForm.isBold">是否加粗</el-checkbox>
  78. <el-checkbox v-model="state.ruleForm.isOpenWebsite">网站公开</el-checkbox>
  79. <el-checkbox v-model="state.ruleForm.isWeibo">微博公开</el-checkbox>
  80. <el-checkbox v-model="state.ruleForm.isWeChat">微信公开</el-checkbox>
  81. <el-checkbox v-model="state.ruleForm.isTop">是否置顶</el-checkbox>
  82. <el-checkbox v-model="state.ruleForm.isPopup">是否飘窗</el-checkbox>
  83. </el-form-item>
  84. </el-col>
  85. <!-- 安全卫士 -->
  86. <template v-if="state.ruleForm.bulletinTypeId === 'aqws'">
  87. <el-col>
  88. <el-form-item label="志愿者类型" prop="safetyTypeId" :rules="[{ required: true, message: '请选择志愿者类型', trigger: 'change' }]">
  89. <el-checkbox-group v-model="state.ruleForm.safetyTypeId">
  90. <el-checkbox :label="item.dicDataName" :value="item.dicDataValue" v-for="item in safetyTypesOptions" :key="item.dicDataValue" />
  91. </el-checkbox-group>
  92. </el-form-item>
  93. </el-col>
  94. <el-col>
  95. <el-form-item label="形式" prop="shape" :rules="[{ required: true, message: '请选择形式', trigger: 'change' }]">
  96. <el-radio-group v-model="state.ruleForm.shape" @change="selectType">
  97. <el-radio :value="0">消息</el-radio>
  98. <el-radio :value="1">视频</el-radio>
  99. </el-radio-group>
  100. </el-form-item>
  101. </el-col>
  102. </template>
  103. <template v-if="state.ruleForm.shape === 0">
  104. <el-col>
  105. <el-form-item label="视频地址" prop="videoPath" :rules="[{ required: false, message: '请填写视频地址', trigger: 'blur' }]">
  106. <el-input v-model="state.ruleForm.videoPath" placeholder="请填写视频地址" clearable></el-input>
  107. </el-form-item>
  108. </el-col>
  109. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  110. <el-form-item label="内容" prop="content" :rules="[{ required: true, message: '请填写内容', trigger: 'blur' }]">
  111. <editor v-model:get-html="state.ruleForm.content" placeholder="请填写内容" height="450px" />
  112. </el-form-item>
  113. </el-col>
  114. </template>
  115. <template v-else>
  116. <el-col>
  117. <el-form-item label="视频封面" prop="videoCoverImgUrl" :rules="[{ required: true, message: '请上传视频封面', trigger: 'change' }]">
  118. <el-upload
  119. class="avatar-uploader"
  120. :action="action"
  121. :show-file-list="false"
  122. :on-success="updateSuccessCareCell"
  123. ref="uploadListRefCareCell"
  124. name="fileData"
  125. :on-error="onUploadError"
  126. accept="image/*"
  127. :disabled="uploadingDisabled"
  128. >
  129. <img v-if="careCellImgUrl" :src="careCellImgUrl" class="avatar" alt="" />
  130. <SvgIcon v-else class="avatar-uploader-icon" name="ele-Plus" size="28px" />
  131. </el-upload>
  132. </el-form-item>
  133. </el-col>
  134. <el-col>
  135. <el-form-item label="视频上传" prop="videoPath" :rules="[{ required: true, message: '请上传视频', trigger: 'change' }]">
  136. <el-upload
  137. ref="uploadVideoRef"
  138. class="upload-demo w100"
  139. :action="action"
  140. :limit="1"
  141. name="fileData"
  142. :on-error="onUploadError"
  143. :on-exceed="handleExceed"
  144. accept="video/mp4,video/mov"
  145. :on-success="updateSuccessVideoPath"
  146. v-model:file-list="fileList"
  147. :disabled="uploadingDisabled"
  148. :before-upload="beforeUpload"
  149. :on-remove="handRemove"
  150. >
  151. <template #trigger>
  152. <el-button type="primary" :loading="uploadLoading" :disabled="uploadingDisabled">
  153. <SvgIcon name="ele-Upload" class="mr5" /> {{ uploadLoading ? '上传中,请稍等...' : '视频上传' }}
  154. </el-button>
  155. </template>
  156. <template #tip>
  157. <div class="el-upload__tip">视频上传:支持上传mp4、mov格式的视频</div>
  158. </template>
  159. </el-upload>
  160. </el-form-item>
  161. </el-col>
  162. </template>
  163. </el-row>
  164. </el-form>
  165. <template #footer>
  166. <span class="dialog-footer">
  167. <el-button @click="closeDialog" class="default-button">取 消</el-button>
  168. <el-button type="primary" @click="onSubmit(ruleFormRef)" :loading="loading">确 定</el-button>
  169. </span>
  170. </template>
  171. </el-dialog>
  172. </template>
  173. <script setup lang="tsx" name="noticeDetail">
  174. import { reactive, ref, defineAsyncComponent, computed } from 'vue';
  175. import { throttle } from '@/utils/tools';
  176. import { ElMessage, FormInstance, UploadFile, UploadFiles } from 'element-plus';
  177. import { disabledDate } from '@/utils/constants';
  178. import { ElCheckbox } from 'element-plus';
  179. import { editSnapshotBulletin, editSnapshotBulletinBase, getSnapshotBulletinDetail } from '@/api/snapshot/info';
  180. // 引入组件
  181. const Editor = defineAsyncComponent(() => import('@/components/Editor/index.vue')); // 富文本编辑器
  182. // 定义子组件向父组件传值/事件
  183. const emit = defineEmits(['updateList']);
  184. // 定义变量内容
  185. const state = reactive<any>({
  186. dialogVisible: false,
  187. ruleForm: {
  188. bulletinTypeId: null, // 公告类型
  189. bulletinTypeName: null, // 公告类型名称
  190. title: null, // 标题
  191. no: null, // 文档编码
  192. bulletinTime: null, // 时间
  193. departmentId: null, // 所属部门ID
  194. departmentName: null, // 所属部门名称
  195. sourceOrgName: null, // 来源单位名称
  196. isOpen: false, // 公开
  197. isBold: false, // 加粗
  198. isOpenWebsite: false, // 网站公开
  199. isWeibo: false, // 微博公开
  200. isWeChat: false, // 微信公开
  201. isTop: false, // 置顶
  202. isPopup: false, // 弹窗
  203. content: null, // 内容
  204. videoPath: null, // 视频地址
  205. sourceMode: '1', // 来源方式 默认自建
  206. safetyTypeId: [], // 志愿者类型
  207. shape: 0, // 公告形式
  208. videoCoverImgUrl: null, // 视频封面
  209. },
  210. });
  211. let loading = ref<boolean>(false); // 加载状态
  212. // 打开弹窗
  213. const ruleFormRef = ref<RefType>();
  214. const bulletinTypeOptions = ref<EmptyArrayType>([]); // 公告类型
  215. const orgsOptions = ref<EmptyArrayType>([]); // 来源单位
  216. const bulletinSource = ref<EmptyArrayType>([]); // 来源
  217. const safetyTypesOptions = ref<EmptyArrayType>([]); // 志愿者类型
  218. const openDialog = async (id: string) => {
  219. loading.value = true;
  220. state.dialogVisible = true;
  221. try {
  222. const [responseAnnounce, result] = await Promise.all([editSnapshotBulletinBase(), getSnapshotBulletinDetail(id)]);
  223. bulletinTypeOptions.value = responseAnnounce.result?.bulletinType ?? [];
  224. orgsOptions.value = responseAnnounce.result?.orgsOptions ?? [];
  225. bulletinSource.value = responseAnnounce.result?.bulletinSource ?? [];
  226. safetyTypesOptions.value = responseAnnounce.result?.safetyTypes ?? [];
  227. state.ruleForm = result.result ?? {};
  228. state.ruleForm.bulletinObj = {
  229. dicDataValue: result.result?.bulletinTypeId,
  230. dicDataName: result.result?.bulletinTypeName,
  231. };
  232. if (result.result?.videoCoverImgUrl) careCellImgUrl.value = import.meta.env.VITE_API_UPLOAD_URL + result.result?.videoCoverImgUrl; // 视频封面
  233. if (result.result?.videoPath) {
  234. //视频地址
  235. fileList.value = [
  236. {
  237. name: '视频',
  238. url: import.meta.env.VITE_API_UPLOAD_URL + result.result?.videoPath,
  239. },
  240. ];
  241. }
  242. loading.value = false;
  243. } catch (error) {
  244. console.log(error);
  245. loading.value = false;
  246. }
  247. };
  248. const orgRef = ref<RefType>(); // 所属部门
  249. const changeOrg = () => {
  250. const currentNode = orgRef.value.getCheckedNodes();
  251. if (currentNode) state.ruleForm.departmentName = currentNode[0].label;
  252. else state.ruleForm.departmentName = null;
  253. };
  254. // 选择类型
  255. const selectType = () => {
  256. if (state.ruleForm.shape === 0) {
  257. state.ruleForm.videoPath = null;
  258. careCellImgUrl.value = '';
  259. state.ruleForm.videoCoverImgUrl = null;
  260. fileList.value = [];
  261. } else {
  262. state.ruleForm.content = null;
  263. }
  264. };
  265. // 上传地址
  266. const action = computed(() => {
  267. return import.meta.env.VITE_API_UPLOAD_URL + '/file/upload?source=hotline';
  268. });
  269. // 上传视频封面
  270. const careCellImgUrl = ref('');
  271. const uploadListRefCareCell = ref<RefType>();
  272. const updateSuccessCareCell = (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  273. if (response.result.path) {
  274. careCellImgUrl.value = import.meta.env.VITE_API_UPLOAD_URL + response.result.path;
  275. // careCellImgUrl.value = URL.createObjectURL(uploadFile.raw!);
  276. state.ruleForm.videoCoverImgUrl = response.result.path;
  277. } else {
  278. uploadListRefCareCell.value.handleRemove(uploadFile);
  279. ElMessage.error('上传失败');
  280. }
  281. setTimeout(() => {
  282. uploadLoading.value = false; // 模拟上传过程
  283. uploadingDisabled.value = false;
  284. }, 100);
  285. };
  286. // 上传视频
  287. const uploadVideoRef = ref<RefType>();
  288. const fileList = ref<UploadFiles>([
  289. /*{
  290. name: '12239_1739325758.mp4',
  291. url: '/file/files_indefinitely?id=08dd8555-5673-4e45-80eb-12cdc7ac8125&expires=1745739172&clientid=hotline&signature=BF5AD8DBEF553CDA7750BBDF01A07C33689FAB83DB5163CADE787E327E366DD426861371167F86296E6DE9C696F65F3C92F3742DE762EE50',
  292. },*/
  293. ]);
  294. const uploadLoading = ref(false);
  295. const beforeUpload = () => {
  296. uploadingDisabled.value = true;
  297. uploadLoading.value = true; // 模拟上传过程
  298. };
  299. const handleExceed = (files: any, uploadFiles: UploadUserFile[]) => {
  300. ElMessage.warning(`当前限制最多上传1个文件,已经上传了${uploadFiles.length}个文件,本次选择了 ${files.length} 个文件。`);
  301. setTimeout(() => {
  302. uploadLoading.value = false; // 模拟上传过程
  303. }, 100);
  304. };
  305. const uploadingDisabled = ref(false);
  306. const updateSuccessVideoPath = (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  307. if (response.result.path) {
  308. state.ruleForm.videoPath = response.result.path;
  309. } else {
  310. uploadVideoRef.value.handleRemove(uploadFile);
  311. ElMessage.error('上传失败');
  312. }
  313. console.log(fileList.value, state.ruleForm.videoPath, '111');
  314. setTimeout(() => {
  315. uploadLoading.value = false; // 模拟上传过程
  316. uploadingDisabled.value = false;
  317. }, 100);
  318. };
  319. // 删除文件
  320. const handRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  321. fileList.value = uploadFiles;
  322. state.ruleForm.videoPath = null;
  323. };
  324. // 上传失败
  325. const onUploadError = (error: Error) => {
  326. try {
  327. const errMessage = JSON.parse(error.message)?.message ?? '上传失败';
  328. ElMessage.error(errMessage);
  329. } catch (e) {
  330. ElMessage.error('上传失败');
  331. }
  332. setTimeout(() => {
  333. uploadLoading.value = false; // 模拟上传过程
  334. uploadingDisabled.value = false;
  335. }, 100);
  336. };
  337. // 保存
  338. const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
  339. if (!formEl) return;
  340. await formEl.validate((valid: boolean) => {
  341. if (!valid) return;
  342. loading.value = true;
  343. const request = {
  344. ...state.ruleForm,
  345. };
  346. editSnapshotBulletin(request)
  347. .then(() => {
  348. loading.value = false;
  349. closeDialog();
  350. emit('updateList');
  351. ElMessage.success('操作成功');
  352. })
  353. .catch(() => {
  354. loading.value = false;
  355. });
  356. });
  357. }, 300);
  358. // 关闭弹窗
  359. const closeDialog = () => {
  360. state.dialogVisible = false;
  361. };
  362. const close = () => {
  363. ruleFormRef.value?.clearValidate();
  364. ruleFormRef.value?.resetFields();
  365. state.ruleForm.bulletinTypeId = null;
  366. };
  367. // 暴露变量
  368. defineExpose({
  369. openDialog,
  370. closeDialog,
  371. });
  372. </script>
  373. <style scoped lang="scss">
  374. .avatar-uploader .avatar {
  375. width: 178px;
  376. height: 178px;
  377. display: block;
  378. }
  379. </style>
  380. <style>
  381. .avatar-uploader .el-upload {
  382. border: 1px dashed var(--el-border-color);
  383. border-radius: 6px;
  384. cursor: pointer;
  385. position: relative;
  386. overflow: hidden;
  387. transition: var(--el-transition-duration-fast);
  388. }
  389. .avatar-uploader .el-upload:hover {
  390. border-color: var(--el-color-primary);
  391. }
  392. .avatar-uploader-icon {
  393. font-size: 28px;
  394. color: #8c939d;
  395. width: 178px;
  396. height: 178px;
  397. text-align: center;
  398. }
  399. </style>