DrillRecord.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <template>
  2. <el-card shadow="hover">
  3. <template #header>
  4. <el-row :gutter="10">
  5. <el-col :span="1.5">
  6. <el-button type="primary" icon="Plus" @click="handleAdd()">新增</el-button>
  7. </el-col>
  8. <el-col :span="1.5">
  9. <el-button type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()"> 删除 </el-button>
  10. </el-col>
  11. </el-row>
  12. </template>
  13. <el-table
  14. v-loading="loading"
  15. :default-sort="{ prop: 'startTime,endTime', order: 'descending' }"
  16. :data="dataList"
  17. @selection-change="handleSelectionChange"
  18. >
  19. <el-table-column type="selection" width="50" align="center" />
  20. <el-table-column label="演练名称" align="center" prop="drillName" />
  21. <el-table-column label="演练单位" align="center" prop="drillUnit" />
  22. <el-table-column label="年度" align="center" prop="year" :show-overflow-tooltip="true" />
  23. <el-table-column label="演练时间" align="center" prop="drillTime" width="160">
  24. <template #default="scope">
  25. <span>{{ scope.row.year }}</span>
  26. </template>
  27. </el-table-column>
  28. <el-table-column label="演练地点" align="center" prop="drillAddress" />
  29. <el-table-column label="演练方案" align="center" prop="drillProject">
  30. <template #default="scope">
  31. <a :href="baseUrl + downLoadApi + scope.row.drillProject[0].url" target="_blank">
  32. <el-text type="primary">{{
  33. scope.row && scope.row.drillProject && scope.row.drillProject[0] ? scope.row.drillProject[0].name : ''
  34. }}</el-text>
  35. </a>
  36. </template>
  37. </el-table-column>
  38. <el-table-column label="演练视频" align="center" prop="drillVideo">
  39. <template #default="scope">
  40. <el-text type="primary" @click="handleShowVideo(scope.row.drillVideo)">查看</el-text>
  41. </template>
  42. </el-table-column>
  43. <el-table-column label="演练图片" align="center" prop="drillPicture">
  44. <template #default="scope">
  45. <el-image
  46. style="width: 100px; height: 100px"
  47. :src="scope.row.url"
  48. :preview-src-list="scope.row.urlList"
  49. :preview-teleported="true"
  50. fit="cover"
  51. />
  52. </template>
  53. </el-table-column>
  54. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  55. <template #default="scope">
  56. <el-text v-hasPermi="['system:menu:Edit']" class="common-btn-text-primary" @click="handleUpdate(scope.row)">编辑</el-text>
  57. <el-text v-hasPermi="['system:menu:Download']" class="common-btn-text-primary" @click="handleDownload(scope.row)">下载</el-text>
  58. <el-text v-hasPermi="['system:menu:Delete']" class="common-btn-text-danger" @click="handleDelete(scope.row)">删除</el-text>
  59. </template>
  60. </el-table-column>
  61. </el-table>
  62. <pagination
  63. v-show="total > queryParams.pageSize"
  64. v-model:page="queryParams.page"
  65. v-model:limit="queryParams.pageSize"
  66. :total="total"
  67. @pagination="getList"
  68. />
  69. </el-card>
  70. <!-- 添加或修改演练记录配置对话框 -->
  71. <el-dialog ref="formDialogRef" v-model="visible" :title="title" width="500px" append-to-body @close="closeDialog">
  72. <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
  73. <el-form-item label="演练名称:" prop="drillName">
  74. <el-input v-model="form.drillName" placeholder="请输入演练名称" />
  75. </el-form-item>
  76. <el-form-item label="演练单位:" prop="drillUnit">
  77. <el-input v-model="form.drillUnit" placeholder="请输入演练单位" />
  78. </el-form-item>
  79. <el-form-item label="年度:" prop="year">
  80. <el-input v-model="form.year" placeholder="请输入演练年度" />
  81. </el-form-item>
  82. <el-form-item label="演练时间:" prop="drillTime">
  83. <el-date-picker
  84. v-model="form.drillTime"
  85. type="datetime"
  86. value-format="YYYY-MM-DD HH:mm:ss"
  87. placeholder="请选择开始时间"
  88. style="width: 100%"
  89. />
  90. </el-form-item>
  91. <el-form-item label="演练地点:" prop="drillAddress">
  92. <el-input v-model="form.drillAddress" placeholder="请选择演练地点" readonly>
  93. <template #append>
  94. <div style="cursor: pointer" @click="openMapDialog">地图定位</div>
  95. </template>
  96. </el-input>
  97. </el-form-item>
  98. <el-form-item label="演练方案:" prop="drillProject">
  99. <FileUpload v-model="form.drillProject" :file-type="['pdf', 'xls', 'xlsx', 'doc', 'docx']" :limit="1" />
  100. </el-form-item>
  101. <el-form-item label="演练视频:" prop="drillVideo">
  102. <FileUpload v-model="form.drillVideo" :file-type="['mp4', 'avi', 'wmv']" :limit="3" :file-size="5 * 1024" />
  103. </el-form-item>
  104. <el-form-item label="演练图片:" prop="drillPicture">
  105. <FileUpload v-model="form.drillPicture" :file-type="['jpg', 'jpeg', 'png']" :limit="12" :file-size="3" />
  106. </el-form-item>
  107. </el-form>
  108. <template #footer>
  109. <div class="dialog-footer">
  110. <el-button type="primary" @click="submitForm">确 定</el-button>
  111. <el-button @click="cancel">取 消</el-button>
  112. </div>
  113. </template>
  114. </el-dialog>
  115. <!--视频弹窗-->
  116. <el-dialog v-model="showVideo" title="视频详情" width="500px" append-to-body>
  117. <div v-for="(item, index) in videoList" :key="index" class="video-box">
  118. <div style="font-weight: bold; font-size: 18px; margin-bottom: 10px">{{ item.name }}</div>
  119. <video :src="baseUrl + downLoadApi + item.url" controls width="100%" height="100%"></video>
  120. </div>
  121. </el-dialog>
  122. <!-- 地图弹窗 -->
  123. <company-map v-model:visible="mapDialogVisible" :address="form.drillAddress" @confirm="handleMapChange"></company-map>
  124. </template>
  125. <script setup lang="ts">
  126. import { listDrill, addDrill, updateDrill, getDrillDetail, deleteDrill } from '@/api/riskPrevention/planManage';
  127. import { to } from 'await-to-js';
  128. import { ref } from 'vue';
  129. import { download2 } from '@/utils/request';
  130. const props = defineProps({
  131. id: String
  132. });
  133. const formRef = ref<ElFormInstance>();
  134. const baseUrl = import.meta.env.VITE_APP_BASE_API;
  135. const downLoadApi = import.meta.env.VITE_APP_BASE_DOWNLOAD_API;
  136. const ids = ref<string[]>([]);
  137. const single = ref(true);
  138. const multiple = ref(true);
  139. const total = ref(0);
  140. const selectedRow = ref(null);
  141. const dataList = ref([]);
  142. const loading = ref(true);
  143. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  144. const emits = defineEmits(['update:modelValue', 'getList']);
  145. let visible = ref(false);
  146. let buttonLoading = ref(false);
  147. let title = ref('');
  148. const queryParams = reactive({
  149. page: 1,
  150. pageSize: 10
  151. });
  152. const form = ref({
  153. drillId: '',
  154. drillName: '',
  155. drillUnit: '',
  156. year: '',
  157. drillTime: '',
  158. drillAddress: '',
  159. lon: '',
  160. lat: '',
  161. drillProject: '',
  162. drillVideo: '',
  163. drillPicture: ''
  164. });
  165. const rules = reactive({
  166. drillName: [{ required: true, message: '演练名称不能为空', trigger: 'blur' }],
  167. drillUnit: [{ required: true, message: '演练单位不能为空', trigger: 'blur' }],
  168. year: [{ required: true, message: '演练年度不能为空', trigger: 'blur' }],
  169. drillTime: [{ required: true, message: '演练时间不能为空', trigger: 'change' }],
  170. drillAddress: [{ required: true, message: '演练地点不能为空', trigger: 'blur' }],
  171. drillProject: [{ required: true, message: '演练方案不能为空', trigger: 'change' }],
  172. drillVideo: [{ required: true, message: '演练视频不能为空', trigger: 'change' }],
  173. drillPicture: [{ required: true, message: '演练图片不能为空', trigger: 'change' }]
  174. });
  175. const showVideo = ref(false);
  176. const videoList = ref([]);
  177. const handleShowVideo = (row) => {
  178. videoList.value = row;
  179. showVideo.value = true;
  180. };
  181. // 获取列表数据
  182. const getList = () => {
  183. loading.value = true;
  184. listDrill({ ...queryParams, planNum: props.id })
  185. .then((res) => {
  186. res.data.forEach((item) => {
  187. if (item.drillPicture && item.drillPicture.length > 0) {
  188. let urlList = [];
  189. item.drillPicture.forEach((item2) => {
  190. urlList.push(baseUrl + downLoadApi + item2.url);
  191. });
  192. item.urlList = urlList;
  193. item.url = urlList[0];
  194. }
  195. });
  196. dataList.value = res.data;
  197. total.value = res.total;
  198. })
  199. .finally(() => {
  200. loading.value = false;
  201. });
  202. };
  203. watch(
  204. () => props.id,
  205. () => {
  206. if (props.id) {
  207. getList();
  208. }
  209. },
  210. {
  211. immediate: true
  212. }
  213. );
  214. /** 取消按钮 */
  215. const cancel = () => {
  216. resetForm();
  217. visible.value = false;
  218. };
  219. /**
  220. * 关闭用户弹窗
  221. */
  222. const closeDialog = () => {
  223. emits('update:modelValue', false);
  224. resetForm();
  225. };
  226. const resetForm = () => {
  227. form.value = {
  228. drillId: '',
  229. drillName: '',
  230. drillUnit: '',
  231. year: '',
  232. drillTime: '',
  233. drillAddress: '',
  234. lon: '',
  235. lat: '',
  236. drillProject: '',
  237. drillVideo: '',
  238. drillPicture: ''
  239. };
  240. formRef.value?.resetFields();
  241. formRef.value?.clearValidate();
  242. };
  243. const handleUpdate = async (row) => {
  244. if (row) {
  245. resetForm();
  246. const res = await getDrillDetail(row.drillId);
  247. form.value = res.data;
  248. visible.value = true;
  249. title.value = '修改演练记录';
  250. }
  251. };
  252. const handleAdd = () => {
  253. resetForm();
  254. visible.value = true;
  255. title.value = '添加演练记录';
  256. };
  257. /**提交按钮 */
  258. const submitForm = () => {
  259. formRef.value?.validate(async (valid) => {
  260. if (valid) {
  261. try {
  262. buttonLoading.value = true;
  263. form.value.drillId ? await updateDrill({ ...form.value, planId: props.id }) : await addDrill({ ...form.value, planId: props.id });
  264. proxy?.$modal.msgSuccess(form.value.drillId ? '修改成功' : '新增成功');
  265. visible.value = false;
  266. getList();
  267. } finally {
  268. buttonLoading.value = false;
  269. }
  270. }
  271. });
  272. };
  273. const handleSelectionChange = (selection) => {
  274. ids.value = selection.map((item) => item.drillId);
  275. selectedRow.value = selection.length === 1 ? selection[0] : null;
  276. single.value = selection.length != 1;
  277. multiple.value = !selection.length;
  278. };
  279. const handleDelete = async (row) => {
  280. const deleteIds = row && row.drillId ? [row?.drillId] : ids.value;
  281. const [err] = await to(proxy?.$modal.confirm('是否确认删除选择的演练记录?') as any);
  282. if (!err) {
  283. await deleteDrill(deleteIds);
  284. getList();
  285. proxy?.$modal.msgSuccess('删除成功');
  286. }
  287. };
  288. // 下载
  289. const handleDownload = (row) => {
  290. if (row.drillProject && row.drillProject.length > 0) {
  291. row.drillProject.forEach((file) => {
  292. download2(baseUrl + downLoadApi + file.url, file.name);
  293. });
  294. }
  295. if (row.drillVideo && row.drillVideo.length > 0) {
  296. row.drillVideo.forEach((file) => {
  297. download2(baseUrl + downLoadApi + file.url, file.name);
  298. });
  299. }
  300. if (row.drillPicture && row.drillPicture.length > 0) {
  301. row.drillPicture.forEach((file) => {
  302. download2(baseUrl + downLoadApi + file.url, file.name);
  303. });
  304. }
  305. };
  306. // 地图定位
  307. const mapDialogVisible = ref(false);
  308. const openMapDialog = () => {
  309. mapDialogVisible.value = true;
  310. };
  311. const handleMapChange = (data) => {
  312. form.value.drillAddress = data.address;
  313. form.value.lon = data.lnglat[0];
  314. form.value.lat = data.lnglat[1];
  315. mapDialogVisible.value = false;
  316. };
  317. </script>
  318. <style lang="scss">
  319. .video-box {
  320. margin-bottom: 10px;
  321. &:last-child {
  322. margin-bottom: 0;
  323. }
  324. }
  325. </style>