index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. <template>
  2. <div class="table-box">
  3. <ProTable ref="proTable" :columns="columns" row-key="id" :request-api="listDataApi" :init-param="initParam">
  4. <template #yuan="scope">
  5. <el-image style="width: 100px" :src="'/api' + scope.row.url" @click="markImg(scope.row)" />
  6. </template>
  7. <!-- 表格 header 按钮 -->
  8. <template #tableHeader="scope">
  9. <el-button type="primary" v-auth="['demo:data:add']" :icon="CirclePlus" @click="openDialog(1, '数据新增')"> 新增 </el-button>
  10. <el-button type="primary" v-auth="['demo:data:import']" :icon="Upload" plain @click="batchAdd"> 导入数据集 </el-button>
  11. <el-button type="primary" v-auth="['demo:data:export']" :icon="Download" plain @click="downloadFile"> 导出 </el-button>
  12. <el-button
  13. type="danger"
  14. v-auth="['demo:data:remove']"
  15. :icon="Delete"
  16. plain
  17. :disabled="!scope.isSelected"
  18. @click="batchDelete(scope.selectedListIds)"
  19. >
  20. 批量删除
  21. </el-button>
  22. </template>
  23. <!-- 表格操作 -->
  24. <template #operation="scope">
  25. <!-- <el-button type="primary" link :icon="EditPen" v-auth="['demo:data:edit']" @click="openDialog(2, '数据标注', scope.row)"> 标注 </el-button> -->
  26. <el-button type="primary" link :icon="EditPen" v-auth="['demo:data:edit']" @click="openDialog(2, '数据编辑', scope.row)"> 编辑 </el-button>
  27. <el-button type="primary" link :icon="View" v-auth="['demo:data:query']" @click="openDialog(3, '数据查看', scope.row)"> 查看 </el-button>
  28. <el-button type="primary" link :icon="Delete" v-auth="['demo:data:remove']" @click="deleteData(scope.row)"> 删除 </el-button>
  29. </template>
  30. </ProTable>
  31. <FormDialog ref="formDialogRef" />
  32. <ImportPicDataset ref="dialogRef" />
  33. <ImgDetect ref="imgDetect" :img="cover" :area="area" :width="width" :height="height" @success="handleImgSuccess"></ImgDetect>
  34. </div>
  35. </template>
  36. <script setup lang="tsx" name="Data">
  37. import { ref, reactive, toRefs } from 'vue'
  38. import { useHandleData } from '@/hooks/useHandleData'
  39. import { useDownload } from '@/hooks/useDownload'
  40. import { ElMessage, ElMessageBox } from 'element-plus'
  41. import ProTable from '@/components/ProTable/index.vue'
  42. import FormDialog from '@/components/FormDialog/index.vue'
  43. import ImportPicDataset from '@/components/ImportPicDataset/index.vue'
  44. import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
  45. import { Delete, EditPen, Download, Upload, View, CirclePlus } from '@element-plus/icons-vue'
  46. // import { fabric } from 'fabric'
  47. import { useDrawArea } from '@/utils/fabric'
  48. import { getDictsApi } from '@/api/modules/system/dictData'
  49. import ImgDetect from '../components/img-detect.vue'
  50. import {
  51. listDataApi,
  52. delDataApi,
  53. addDataApi,
  54. updateDataApi,
  55. importTemplateApi,
  56. importDataDataApi,
  57. exportDataApi,
  58. getDataApi
  59. } from '@/api/modules/demo/data'
  60. const imgDetect = ref()
  61. const state = reactive({
  62. area: '' as string,
  63. width: 1920 as number,
  64. height: 1080 as number,
  65. cover: ''
  66. })
  67. const { area, width, height, cover } = toRefs(state)
  68. // ProTable 实例
  69. const proTable = ref<ProTableInstance>()
  70. // const getImageUrl = name => {
  71. // return new URL(name, import.meta.url).href
  72. // }
  73. const initParam = reactive({ type: 1 })
  74. // 删除数据管理信息
  75. const deleteData = async (params: any) => {
  76. await useHandleData(delDataApi, params.id, `删除【${params.name}】数据`)
  77. proTable.value?.getTableList()
  78. }
  79. // 标注图片
  80. const markImg = data => {
  81. state.cover = '/api' + data.url
  82. // area 代表后端的传来的标注数据
  83. const area = []
  84. if (state.cover != '') {
  85. if (area.length != 0) {
  86. handleImgSuccess(area)
  87. }
  88. imgDetect.value.visible = true
  89. } else {
  90. ElMessage.warning('缺失图像,暂时不能进行区域添加!')
  91. }
  92. }
  93. const getList = () => {
  94. useDrawArea({
  95. src: state.cover,
  96. width: state.width,
  97. height: state.height,
  98. area: state.area
  99. })
  100. .then(url => {
  101. state.cover = url as string
  102. })
  103. .catch(error => {
  104. console.log(error)
  105. })
  106. }
  107. const handleImgSuccess = data => {
  108. data.forEach(item => {
  109. const area = item.split(';')
  110. // try=tly blx=tlx brx=trx bry=bly
  111. const tlx = Math.round(Number(area[0]) * 1920)
  112. const tly = Math.round(Number(area[1]) * 1080)
  113. const trx = tlx + Math.round(Number(area[2]) * 1920)
  114. const bly = tly + Math.round(Number(area[3]) * 1080)
  115. state.area += `${tlx};${tly};${trx};${tly};${trx};${bly};${tlx};${bly},`
  116. })
  117. // console.log('state.area', state.area)
  118. state.area.slice(0, -1)
  119. getList()
  120. }
  121. const labeledTypeData = [
  122. {
  123. label: '是',
  124. value: true
  125. },
  126. {
  127. label: '否',
  128. value: false
  129. }
  130. ]
  131. // 批量删除数据管理信息
  132. const batchDelete = async (ids: string[]) => {
  133. await useHandleData(delDataApi, ids, '删除所选数据信息')
  134. proTable.value?.clearSelection()
  135. proTable.value?.getTableList()
  136. }
  137. // 导出数据管理列表
  138. const downloadFile = async () => {
  139. ElMessageBox.confirm('确认导出数据管理数据?', '温馨提示', { type: 'warning' }).then(() =>
  140. useDownload(exportDataApi, '数据管理列表', proTable.value?.searchParam)
  141. )
  142. }
  143. // 批量添加数据管理
  144. const dialogRef = ref<InstanceType<typeof ImportPicDataset> | null>(null)
  145. const batchAdd = () => {
  146. const params = {
  147. title: '数据管理添加数据集',
  148. tempApi: importTemplateApi,
  149. importApi: importDataDataApi,
  150. getTableList: proTable.value?.getTableList
  151. }
  152. dialogRef.value?.acceptParams(params)
  153. }
  154. const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
  155. // 打开弹框的功能
  156. const openDialog = async (type: number, title: string, row?: any) => {
  157. let res = { data: {} }
  158. if (row?.id) {
  159. res = await getDataApi(row?.id || null)
  160. }
  161. // 重置表单
  162. setFormItems()
  163. const params = {
  164. title,
  165. width: 580,
  166. isEdit: type !== 3,
  167. itemsOptions: formItems,
  168. model: type == 1 ? {} : res.data,
  169. api: type == 1 ? addDataApi : updateDataApi,
  170. getTableList: proTable.value?.getTableList
  171. }
  172. formDialogRef.value?.openDialog(params)
  173. }
  174. // 表格配置项
  175. const columns = reactive<ColumnProps<any>[]>([
  176. { type: 'selection', fixed: 'left', width: 70 },
  177. { prop: 'yuan', label: '原图', width: 200 },
  178. {
  179. prop: 'batchNum',
  180. label: '批次号',
  181. search: {
  182. el: 'input'
  183. },
  184. width: 120
  185. },
  186. {
  187. prop: 'name',
  188. label: '名称',
  189. search: {
  190. el: 'input'
  191. },
  192. width: 120
  193. },
  194. {
  195. prop: 'objectType',
  196. label: '目标类型',
  197. search: {
  198. el: 'input'
  199. },
  200. width: 120
  201. },
  202. {
  203. prop: 'objectSubtype',
  204. label: '目标子类型',
  205. search: {
  206. el: 'input'
  207. },
  208. width: 120
  209. },
  210. {
  211. prop: 'scene',
  212. label: '场景',
  213. search: {
  214. el: 'input'
  215. },
  216. width: 120
  217. },
  218. {
  219. prop: 'dataSource',
  220. label: '数据源',
  221. search: {
  222. el: 'input'
  223. },
  224. width: 120
  225. },
  226. {
  227. prop: 'gatherSpot',
  228. label: '采集地点',
  229. search: {
  230. el: 'input'
  231. },
  232. width: 120
  233. },
  234. {
  235. prop: 'gatherTime',
  236. label: '采集时间',
  237. search: {
  238. el: 'date-picker',
  239. props: { type: 'datetimerange', valueFormat: 'YYYY-MM-DD HH:mm:ss' }
  240. },
  241. width: 120
  242. },
  243. {
  244. prop: 'dataType',
  245. label: '数据类型',
  246. enum: () => getDictsApi('data_type'),
  247. search: {
  248. el: 'tree-select'
  249. },
  250. fieldNames: { label: 'dictLabel', value: 'dictValue' },
  251. width: 120
  252. },
  253. // {
  254. // prop: 'fileType',
  255. // label: '文件类型',
  256. // search: {
  257. // el: 'input'
  258. // },
  259. // width: 120
  260. // },
  261. // {
  262. // prop: 'increment',
  263. // label: '扩增方式',
  264. // search: {
  265. // el: 'input'
  266. // },
  267. // width: 120
  268. // },
  269. {
  270. prop: 'labeled',
  271. label: '是否标注',
  272. search: {
  273. el: 'input'
  274. },
  275. width: 120
  276. },
  277. { prop: 'operation', label: '操作', width: 230, fixed: 'right' }
  278. ])
  279. // 表单配置项
  280. let formItems: ProForm.ItemsOptions[] = []
  281. const setFormItems = () => {
  282. formItems = [
  283. {
  284. label: '原图',
  285. prop: 'url',
  286. compOptions: {
  287. elTagName: 'img-upload',
  288. placeholder: '请选择上传原图'
  289. }
  290. },
  291. {
  292. label: '批次号',
  293. prop: 'batchNum',
  294. rules: [{ required: true, message: '批次号不能为空', trigger: 'blur' }],
  295. compOptions: {
  296. placeholder: '请输入批次号'
  297. }
  298. },
  299. {
  300. label: '名称',
  301. prop: 'name',
  302. rules: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
  303. compOptions: {
  304. placeholder: '请输入名称'
  305. }
  306. },
  307. {
  308. label: '目标类型',
  309. prop: 'objectType',
  310. rules: [{ required: true, message: '目标类型不能为空', trigger: 'blur' }],
  311. compOptions: {
  312. placeholder: '请输入目标类型'
  313. }
  314. },
  315. {
  316. label: '目标子类型',
  317. prop: 'objectSubtype',
  318. rules: [{ required: true, message: '目标子类型不能为空', trigger: 'blur' }],
  319. compOptions: {
  320. placeholder: '请输入目标子类型'
  321. }
  322. },
  323. {
  324. label: '场景',
  325. prop: 'scene',
  326. rules: [{ required: true, message: '场景不能为空', trigger: 'blur' }],
  327. compOptions: {
  328. placeholder: '请输入场景'
  329. }
  330. },
  331. {
  332. label: '数据源',
  333. prop: 'dataSource',
  334. rules: [{ required: true, message: '数据源不能为空', trigger: 'blur' }],
  335. compOptions: {
  336. placeholder: '请输入数据源'
  337. }
  338. },
  339. {
  340. label: '采集地点',
  341. prop: 'gatherSpot',
  342. rules: [{ required: true, message: '采集地点不能为空', trigger: 'blur' }],
  343. compOptions: {
  344. placeholder: '请输入采集地点'
  345. }
  346. },
  347. {
  348. label: '采集时间',
  349. prop: 'gatherTime',
  350. rules: [{ required: true, message: '采集时间不能为空', trigger: 'change' }],
  351. compOptions: {
  352. elTagName: 'date-picker',
  353. type: 'datetime',
  354. valueFormat: 'YYYY-MM-DD HH:mm:ss',
  355. placeholder: '请选择采集时间'
  356. }
  357. },
  358. {
  359. label: '数据类型',
  360. prop: 'dataType',
  361. rules: [{ required: true, message: '数据类型不能为空', trigger: 'change' }],
  362. compOptions: {
  363. elTagName: 'select',
  364. labelKey: 'dictLabel',
  365. valueKey: 'dictValue',
  366. enum: () => getDictsApi('data_type'),
  367. placeholder: '请选择数据类型'
  368. }
  369. },
  370. {
  371. label: '是否标注',
  372. prop: 'labeled',
  373. rules: [{ required: true, message: '请选择是否标注' }],
  374. compOptions: {
  375. elTagName: 'radio-group',
  376. enum: labeledTypeData
  377. }
  378. }
  379. ]
  380. }
  381. </script>