image-form.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. <template>
  2. <div class="hc-layout-box">
  3. <div v-if="dataType === 1" :style="`width:${leftWidth}px;`" class="hc-layout-left-box">
  4. <div class="hc-project-box">
  5. <div class="hc-project-icon-box">
  6. <HcIcon name="stack" />
  7. </div>
  8. <div class="ml-2 project-name-box">
  9. <span class="text-xl text-cut project-alias">{{ projectInfo.projectAlias }}</span>
  10. <div class="text-xs text-cut project-name">{{ projectInfo.name }}</div>
  11. </div>
  12. </div>
  13. <div class="hc-tree-box">
  14. <el-scrollbar>
  15. <WbsTree
  16. :auto-expand-keys="TreeAutoExpandKeys" :contract-id="contractId" :project-id="projectId" :class-id="dataId"
  17. @nodeTap="nodeWbsElTreeClick"
  18. />
  19. </el-scrollbar>
  20. </div>
  21. <!-- 左右拖动 -->
  22. <div class="horizontal-drag-line" @mousedown="onmousedown" />
  23. </div>
  24. <div v-loading="queryByLoading" class="hc-layout-content-box" element-loading-text="获取数据中...">
  25. <HcCard
  26. :scrollbar="false"
  27. :title="`${treeItemInfo?.title ?? ''} 上传${fileType === 1 ? '视频' : fileType === 2 ? '图片' : ''}`"
  28. action-size="lg"
  29. >
  30. <el-alert
  31. v-if="dataType === 1 && !wbsId" :closable="false" effect="dark" style="margin-bottom: 14px"
  32. title="请先在左边项目树,选择一个节点" type="warning"
  33. />
  34. <el-row :gutter="20" class="hc-form-row-box">
  35. <el-col :span="10">
  36. <el-scrollbar>
  37. <el-form ref="formRef" :model="formValue" :rules="rules" label-width="auto" size="large">
  38. <el-form-item label="上传日期" prop="uploadTime">
  39. <el-date-picker
  40. v-model="formValue.uploadTime" format="YYYY-MM-DD" type="date"
  41. value-format="YYYY-MM-DD"
  42. />
  43. </el-form-item>
  44. <el-form-item label="上传文件" prop="imageUrl">
  45. <div class="w-full">
  46. <HcUploads
  47. :accept="fileType === 1 ? videoAccept : fileType === 2 ? imageAccept : null"
  48. :file-list="uploadFileList"
  49. :limit="fileType === 2 ? 10 : 1"
  50. :size="fileType === 2 ? 30 : 500" :viewer="false"
  51. action="upload-file" @change="uploadsChange" @del="uploadsDel"
  52. @preview="uploadsPreview" @progress="uploadsProgress"
  53. />
  54. </div>
  55. <el-image-viewer
  56. v-if="previewModal && fileType === 2" :initial-index="initialIndex"
  57. :url-list="previewFileList" @close="previewModalClose"
  58. />
  59. <el-dialog
  60. v-model="previewVideoModal" :before-close="previewModalClose"
  61. destroy-on-close
  62. width="62rem"
  63. >
  64. <video
  65. :src="previewVideoUrl" autoplay="autoplay" class="preview-video"
  66. controls="controls"
  67. >
  68. 您的浏览器不支持 video
  69. </video>
  70. </el-dialog>
  71. <el-alert
  72. v-if="fileType === 1"
  73. :closable="false"
  74. title="请上传MP4、MOV格式的视频文件,文件大小不超过500M,只能上传1个视频文件"
  75. type="error"
  76. />
  77. <el-alert
  78. v-if="fileType === 2"
  79. :closable="false"
  80. title="请上传JPG/JPEG、PNG格式的图片文件,文件大小不超过30M,最多10张图片文件"
  81. type="error"
  82. />
  83. </el-form-item>
  84. <el-form-item label="题名" prop="title">
  85. <el-input v-model="formValue.title" placeholder="请输入题名" />
  86. </el-form-item>
  87. <div class="flex">
  88. <el-form-item
  89. :class="fileType === 2 ? 'mr-4' : ''" class="flex-1" label="拍摄者"
  90. prop="shootingUser"
  91. >
  92. <el-input v-model="formValue.shootingUser" placeholder="请输入拍摄者" />
  93. </el-form-item>
  94. <el-form-item v-if="fileType === 2" class="flex-1 ml-4" label="照片号">
  95. <el-input v-model="formValue.photoCode" placeholder="请输入照片号" />
  96. </el-form-item>
  97. </div>
  98. <el-form-item label="拍摄时间" prop="shootingTimeStr">
  99. <el-date-picker
  100. v-model="formValue.shootingTimeStr" format="YYYY-MM-DD" type="date"
  101. value-format="YYYY-MM-DD"
  102. />
  103. </el-form-item>
  104. <div v-if="fileType === 2" class="flex">
  105. <el-form-item class="flex-1 mr-4" label="底片号" prop="filmCode">
  106. <el-input v-model="formValue.filmCode" placeholder="请输入底片号" />
  107. </el-form-item>
  108. <el-form-item class="flex-1 ml-4" label="参见号" prop="seeAlsoCode">
  109. <el-input v-model="formValue.seeAlsoCode" placeholder="请输入参见号" />
  110. </el-form-item>
  111. </div>
  112. <el-form-item label="文字说明" prop="textContent">
  113. <el-input
  114. v-model="formValue.textContent" :autosize="{ minRows: 3, maxRows: 5 }"
  115. placeholder="请输入文字说明" type="textarea"
  116. />
  117. </el-form-item>
  118. </el-form>
  119. </el-scrollbar>
  120. </el-col>
  121. <el-col :span="14">
  122. <HcTable v-if="dataType === 2" :column="tableColumn" :datas="tableData" />
  123. </el-col>
  124. </el-row>
  125. <template #action>
  126. <el-button
  127. :disabled="uploadsLoading || (dataType === 1 && !wbsId)" :loading="saveLoading" hc-btn
  128. type="primary" @click="saveClick"
  129. >
  130. <HcIcon name="save" />
  131. <span>保存</span>
  132. </el-button>
  133. <el-button
  134. v-if="dataType === 2" :disabled="uploadsLoading" :loading="saveLoading" hc-btn
  135. @click="saveLogClick"
  136. >
  137. <HcIcon name="save" />
  138. <span>保存并再次添加</span>
  139. </el-button>
  140. <el-button hc-btn @click="toBackClick">
  141. <HcIcon name="arrow-go-back" />
  142. <span>返回</span>
  143. </el-button>
  144. </template>
  145. </HcCard>
  146. </div>
  147. </div>
  148. </template>
  149. <script setup>
  150. import { onMounted, ref, watch } from 'vue'
  151. import { useAppStore } from '~src/store'
  152. import { useRoute, useRouter } from 'vue-router'
  153. import WbsTree from './components/WbsTree.vue'
  154. import imageApi from '~api/other-file/imageData'
  155. import { getStoreValue, setStoreValue } from '~src/utils/storage'
  156. import { arrToKey, deepClone, formValidate, getObjValue, getRandom } from 'js-fast-way'
  157. import { dateFormat } from '~src/utils/tools'
  158. import ossApi from '~api/oss'
  159. //变量
  160. const router = useRouter()
  161. const useRoutes = useRoute()
  162. const useAppState = useAppStore()
  163. const projectId = ref(useAppState.getProjectId)
  164. const contractId = ref(useAppState.getContractId)
  165. const projectInfo = ref(useAppState.getProjectInfo)
  166. const isCollapse = ref(useAppState.getCollapse)
  167. const userInfo = ref(useAppState.getUserInfo)
  168. //路由参数
  169. const routerQuery = useRoutes?.query
  170. //存储目录格式 1按部位存储,2按日期存储
  171. const dataId = routerQuery?.id || ''
  172. const wbsNodeIds = routerQuery?.wbsId || ''
  173. const classifyId = routerQuery?.classifyId || ''
  174. const dataType = parseInt(routerQuery?.dataType + '') || 1
  175. const fileType = parseInt(routerQuery?.fileType + '') || 2
  176. const toDayDate = dateFormat(new Date(), 'yyyy-MM-dd')
  177. const userRealName = ref('')
  178. //监听
  179. watch(() => [
  180. useAppState.getCollapse,
  181. useAppState.getUserInfo,
  182. useAppState.getContractId,
  183. ], ([Collapse, UserInfo, ContractId]) => {
  184. isCollapse.value = Collapse
  185. userRealName.value = UserInfo['real_name']
  186. formValue.value.shootingUser = UserInfo['real_name']
  187. contractId.value = ContractId
  188. })
  189. watch(contractId, (val) => {
  190. if (val) {
  191. router.push({
  192. path: '/other-file/image-data',
  193. })
  194. }
  195. })
  196. //自动展开缓存
  197. const TreeAutoExpandKeys = ref([])
  198. //渲染完成
  199. onMounted(() => {
  200. if (dataType === 1) {
  201. TreeAutoExpandKeys.value = getStoreValue('TreeExpandKeys')
  202. }
  203. formDataFormat({})
  204. queryById()
  205. formValue.value.shootingUser = userInfo.value['real_name']
  206. if (wbsNodeIds) {
  207. getFileTitleNamedata(wbsNodeIds)
  208. }
  209. })
  210. //详情
  211. const queryByLoading = ref(false)
  212. const queryById = async () => {
  213. if (dataId) {
  214. queryByLoading.value = true
  215. const { error, code, data } = await imageApi.queryById({ id: dataId })
  216. //判断状态
  217. queryByLoading.value = false
  218. if (!error && code === 200) {
  219. formDataFormat(getObjValue(data))
  220. }
  221. }
  222. }
  223. //获取题名
  224. const getFileTitleNamedata = async (wbsNodeIds) => {
  225. const { error, code, data } = await imageApi.getFileTitleName({ pKeyId: wbsNodeIds })
  226. //判断状态
  227. if (!error && code === 200) {
  228. let arr = Object.keys(data)
  229. if (arr.length > 0) {
  230. formValue.value.title = data
  231. } else {
  232. formValue.value.title = ''
  233. }
  234. }
  235. }
  236. //项目树被点击
  237. const wbsId = ref('')
  238. const treeItemInfo = ref({})
  239. const nodeWbsElTreeClick = ({ data, keys }) => {
  240. if (data.leaf === true) {
  241. treeItemInfo.value = data
  242. wbsId.value = data['primaryKeyId']
  243. formValue.value.wbsId = data['primaryKeyId']
  244. getFileTitleNamedata(data['primaryKeyId'])
  245. //缓存自动展开
  246. TreeAutoExpandKeys.value = keys
  247. setStoreValue('TreeExpandKeys', keys)
  248. } else {
  249. wbsId.value = ''
  250. treeItemInfo.value = {}
  251. formValue.value.wbsId = null
  252. }
  253. }
  254. //表单相关数据
  255. const formRef = ref(null)
  256. const formValue = ref({
  257. uploadTime: toDayDate,
  258. shootingTimeStr: toDayDate,
  259. shootingUser: '',
  260. filmCode: '',
  261. seeAlsoCode: '',
  262. })
  263. const rules = {
  264. uploadTime: {
  265. required: true,
  266. trigger: 'blur',
  267. message: '请选择上传日期',
  268. },
  269. imageUrl: {
  270. required: true,
  271. message: '请先上传文件',
  272. },
  273. title: {
  274. required: true,
  275. trigger: 'blur',
  276. message: '请输入题名',
  277. },
  278. shootingUser: {
  279. required: true,
  280. trigger: 'blur',
  281. message: '请输入拍摄者',
  282. },
  283. shootingTimeStr: {
  284. required: true,
  285. trigger: 'blur',
  286. message: '请选择拍摄时间',
  287. },
  288. }
  289. //表单默认数据
  290. const formDataFormat = (info) => {
  291. //表单数据
  292. formValue.value = {
  293. uploadTime: toDayDate,
  294. shootingTimeStr: toDayDate,
  295. ...info,
  296. type: fileType || '2',
  297. wbsId: info?.wbsId || wbsNodeIds,
  298. classifyId: info?.classifyId || classifyId,
  299. projectId: info?.projectId || projectId.value,
  300. contractId: info?.contractId || contractId.value,
  301. }
  302. //原始文件地址
  303. let imageUrl = info['imageUrl'] || ''
  304. let imageUrlArr = imageUrl ? imageUrl.split(',') : []
  305. //PDF地址
  306. let pdfUrl = info['pdfUrl'] || ''
  307. let pdfUrlArr = pdfUrl ? pdfUrl.split(',') : []
  308. //处理数据
  309. if (imageUrlArr.length > 0) {
  310. //状态处理
  311. let InfoPdfUrl = pdfUrlArr.length === imageUrlArr.length
  312. //遍历数据
  313. for (let i = 0; i < imageUrlArr.length; i++) {
  314. let item = imageUrlArr[i]
  315. uploadFileList.value.push({
  316. name: getRandom(),
  317. url: item,
  318. page: '',
  319. pdfUrl: InfoPdfUrl ? pdfUrlArr[i] : '',
  320. })
  321. }
  322. } else {
  323. uploadFileList.value = []
  324. }
  325. }
  326. //上传组件参数
  327. const imageAccept = 'image/png,image/jpg,image/jpeg'
  328. const videoAccept = 'video/*'
  329. //上传文件的相关数据
  330. const previewFileList = ref([])
  331. const uploadFileList = ref([])
  332. //上传的文件结果
  333. const uploadsChange = ({ fileList }) => {
  334. uploadFileList.value = fileList
  335. }
  336. //上传进度
  337. const uploadsLoading = ref(false)
  338. const uploadsProgress = (val) => {
  339. uploadsLoading.value = val
  340. }
  341. //预览上传的文件
  342. const previewModal = ref(false)
  343. const previewVideoModal = ref(false)
  344. const initialIndex = ref(-1)
  345. const previewVideoUrl = ref('')
  346. const uploadsPreview = ({ index, fileArr }) => {
  347. if (fileType === 2) {
  348. previewFileList.value = fileArr
  349. initialIndex.value = index
  350. previewModal.value = true
  351. } else if (fileType === 1) {
  352. previewVideoUrl.value = fileArr[index]
  353. previewVideoModal.value = true
  354. }
  355. }
  356. //预览关闭
  357. const previewModalClose = () => {
  358. initialIndex.value = -1
  359. previewModal.value = false
  360. previewVideoModal.value = false
  361. }
  362. //删除上传的文件
  363. const uploadsDel = async ({ link }) => {
  364. const arrUrl = link.split('.com//')
  365. if (arrUrl.length > 0) {
  366. await ossApi.removeFile({ fileName: arrUrl[1] }, false)
  367. }
  368. }
  369. //上传记录表格
  370. const tableColumn = ref([
  371. { name: '上传日期', key: 'uploadTime' },
  372. { name: '题名', key: 'title' },
  373. { name: '拍摄者', key: 'shootingUser' },
  374. { name: '拍摄时间', key: 'shootingTimeStr' },
  375. ])
  376. const tableData = ref([])
  377. //保存
  378. const saveLoading = ref(false)
  379. const saveClick = () => {
  380. verifyFormData(false)
  381. }
  382. //保存并添加记录
  383. const saveLogClick = () => {
  384. verifyFormData(true)
  385. }
  386. //表单效验
  387. const shootingTimeStr = ref('')
  388. const verifyFormData = async (log) => {
  389. const formData = formValue.value
  390. const fileList = uploadFileList.value
  391. //处理文件
  392. let imageUrl = '', pdfUrl = ''
  393. if (fileList.length > 0) {
  394. imageUrl = arrToKey(fileList, 'url', ',')
  395. pdfUrl = arrToKey(fileList, 'pdfUrl', ',')
  396. }
  397. //设置数据
  398. formData.imageUrl = imageUrl
  399. formData.pdfUrl = pdfUrl
  400. shootingTimeStr.value = formData['shootingTimeStr'] || ''
  401. //验证数据
  402. if (!formData?.wbsId && dataType === 1) {
  403. window.$message?.warning('请先选择节点')
  404. } else {
  405. const res = await formValidate(formRef.value)
  406. if (res) {
  407. if (formData?.id) {
  408. updateImageclassifyFile(formData, log)
  409. } else {
  410. addImageclassifyFile(formData, log)
  411. }
  412. } else {
  413. window.$message?.warning('请先完善表单')
  414. }
  415. }
  416. }
  417. //新增资料
  418. const addImageclassifyFile = async (formData, log) => {
  419. saveLoading.value = true
  420. const { error, code } = await imageApi.addImageclassifyFile(formData)
  421. //判断状态
  422. saveLoading.value = false
  423. if (!error && code === 200) {
  424. window.$message?.success('保存成功')
  425. if (log) {
  426. tableData.value.push(deepClone(formValue.value))
  427. formDataFormat({})
  428. } else {
  429. toBackClick()
  430. }
  431. }
  432. }
  433. //修改资料
  434. const updateImageclassifyFile = async (formData, log) => {
  435. saveLoading.value = true
  436. const { error, code } = await imageApi.updateImageclassifyFile(formData)
  437. //判断状态
  438. saveLoading.value = false
  439. if (!error && code === 200) {
  440. window.$message?.success('保存成功')
  441. if (log) {
  442. tableData.value.push(deepClone(formValue.value))
  443. formDataFormat({})
  444. } else {
  445. toBackClick()
  446. }
  447. }
  448. }
  449. //处理data类型的自动展开
  450. const dataNodeExpandKeys = () => {
  451. let TimeStr = shootingTimeStr.value || ''
  452. let TimeArr = TimeStr.split('-')
  453. if (TimeStr && TimeArr.length > 0) {
  454. let timeKey = TimeArr[0] + '-' + TimeArr[1]
  455. setStoreValue('TreeExpandedKeys', [timeKey])
  456. }
  457. }
  458. //返回上页
  459. const toBackClick = () => {
  460. if (dataType === 1) {
  461. //NodeExpandKeys()
  462. } else if (dataType === 2) {
  463. dataNodeExpandKeys()
  464. }
  465. //返回上级
  466. router.push({
  467. path: '/other-file/image-view',
  468. query: {
  469. fileType: fileType,
  470. type: dataType,
  471. id: classifyId,
  472. },
  473. })
  474. setTimeout(() => {
  475. window?.location?.reload() //刷新页面
  476. }, 1000)
  477. }
  478. //左右拖动,改变树形结构宽度
  479. const leftWidth = ref(382)
  480. const onmousedown = () => {
  481. const leftNum = isCollapse.value ? 142 : 272
  482. document.onmousemove = (ve) => {
  483. let diffVal = ve.clientX - leftNum
  484. if (diffVal >= 310 && diffVal <= 900) {
  485. leftWidth.value = diffVal
  486. }
  487. }
  488. document.onmouseup = () => {
  489. document.onmousemove = null
  490. document.onmouseup = null
  491. }
  492. }
  493. </script>
  494. <style lang="scss" scoped>
  495. @import '../../styles/other-file/image-form.scss';
  496. </style>
  497. <style lang="scss">
  498. .hc-form-row-box {
  499. height: 100%;
  500. .el-col {
  501. height: 100%;
  502. .el-date-editor {
  503. --el-date-editor-width: 100%;
  504. .el-input__wrapper {
  505. width: 100%;
  506. }
  507. }
  508. .el-alert {
  509. margin-top: 14px;
  510. --el-alert-padding: 0px 16px;
  511. }
  512. }
  513. }
  514. .preview-video {
  515. width: 100%;
  516. }
  517. </style>