| | |
| | | <template> |
| | | <div class="main"> |
| | | <div class="filters" v-if="false"> |
| | | <el-select |
| | | v-for="(key_select, index_select) of filters.keys()" |
| | | :placeholder="key_select.text" |
| | | > |
| | | <el-option |
| | | v-for="(key_option, index_option) in filters.get(key_select.key)" |
| | | :key="key_option.key" |
| | | :value="key_option.value" |
| | | :label="key_option.label" |
| | | <FYImageSelectDialog |
| | | v-bind="$attrs" |
| | | v-loading="loading" |
| | | title="场景图片" |
| | | :typeList="typesList" |
| | | :typeImgMap="typesMap" |
| | | :onContextMenu="showContextMenu" |
| | | contextMenuStr="右键点击图片修改分类" |
| | | ></FYImageSelectDialog> |
| | | <!-- <div |
| | | |
| | | @click="closeContextMenu" |
| | | @contextmenu="closeContextMenu" |
| | | class="container" |
| | | > --> |
| | | <div |
| | | v-if="showMenu && !menuLoading" |
| | | ref="menu" |
| | | :style="menuStyle" |
| | | class="context-menu" |
| | | > |
| | | <template v-for="(item, index) in menuItems" :key="index"> |
| | | <el-popover v-if="item.children" placement="right-start" trigger="hover"> |
| | | <template #reference> |
| | | <el-row justify="space-between" class="menu-item"> |
| | | <span>{{ item.label }}</span> |
| | | <el-icon><ArrowRight /></el-icon> |
| | | </el-row> |
| | | </template> |
| | | <div |
| | | v-for="(item1, index1) in item.children" |
| | | :key="index1" |
| | | class="menu-item" |
| | | @click.stop="handleMenuItem(item1)" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | </div> |
| | | <div class="btns" v-if="!readonly"> |
| | | <el-button size="small" type="primary" @click="sendSelectedImg(true)">确定</el-button> |
| | | <el-button size="small" type="primary" @click="sendSelectedImg(false)">取消</el-button> |
| | | </div> |
| | | {{ item1.label }} |
| | | </div> |
| | | </el-popover> |
| | | |
| | | <div class="center"> |
| | | <el-descriptions> |
| | | <el-descriptions-item label="总数"> |
| | | <span>{{ this.imgUrlList.length }}</span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-tabs v-model="activeId" type="card"> |
| | | <el-tab-pane |
| | | v-for="item in typeList" |
| | | :label="item.businesstype" |
| | | :name="item.businesstypeid" |
| | | > |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | <el-empty v-if="isEmpty" description="暂无记录" /> |
| | | <div class="imgs"> |
| | | <el-image |
| | | v-for="(img, i) in typeImgMap.get(activeId)" |
| | | :class="[Boolean(img.isSelect) ? 'selected' : 'noActive', 'image']" |
| | | fit="cover" |
| | | :src="img.url" |
| | | lazy |
| | | @click="onSelect(img, i)" |
| | | /> |
| | | <div class="menu-item" @click.stop="handleMenuItem(item)" v-else> |
| | | {{ item.label }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | <!-- </div> --> |
| | | </template> |
| | | <script> |
| | | import problemApi from '@/api/fysp/problemApi.js'; |
| | | import mediafileApi from '@/api/fysp/mediafileApi.js'; |
| | | import { $fysp } from '@/api/index.js'; |
| | | import { enumMediaFile } from '@/enum/mediaFile.js'; |
| | | |
| | | export default { |
| | | props: { |
| | | filters: Map, |
| | | // 是否以只读的形式查看当前页面 |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | subtask: { |
| | | type: Object, |
| | | efault: {} |
| | | }, |
| | | inspectionGuid: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | defaultFile: { |
| | | type: Array, |
| | | default: () => [] |
| | | type: Object |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | // 无数据 |
| | | isEmpty: false, |
| | | isClose: false, |
| | | isAll: false, |
| | | activeId: '', |
| | | typeList: [ |
| | | // { businesstypeid: 5, businesstype: '常规记录' }, |
| | | // { businesstypeid: 3, businesstype: '监测设备' }, |
| | | // { businesstypeid: 7, businesstype: '铭牌' }, |
| | | // { businesstypeid: 51, businesstype: '扩展类一' }, |
| | | // { businesstypeid: 52, businesstype: '扩展类二' }, |
| | | // { businesstypeid: 53, businesstype: '扩展类三' }, |
| | | // { businesstypeid: 54, businesstype: '扩展类四' }, |
| | | // { businesstypeid: 55, businesstype: '扩展类五' }, |
| | | // { businesstypeid: 56, businesstype: '扩展类六' }, |
| | | // { businesstypeid: 57, businesstype: '扩展类七' }, |
| | | // { businesstypeid: 58, businesstype: '扩展类八' }, |
| | | // { businesstypeid: 59, businesstype: '扩展类九' }, |
| | | // { businesstypeid: 60, businesstype: '扩展类十' } |
| | | ], |
| | | typeImgMap: new Map(), |
| | | imgUrlList: [] |
| | | typesList: [], |
| | | typesMap: new Map(), |
| | | loading: true, |
| | | // 右键图片弹出菜单控制 |
| | | showMenu: false, |
| | | menuLoading: true, |
| | | menuStyle: undefined, |
| | | allMoveActions: [], |
| | | closeContextMenuListenr: undefined, |
| | | // 右键选中的图片 |
| | | selectedFileElement: undefined, |
| | | selectedFile: undefined, |
| | | selectedIndex: undefined, |
| | | selectedTypeId: undefined |
| | | }; |
| | | }, |
| | | watch: { |
| | | defaultFile: { |
| | | handler(newFileList, oldFileList) { |
| | | if (this.isClose) { |
| | | return; |
| | | computed: { |
| | | menuItems() { |
| | | return [ |
| | | // { label: '复制到剪贴板', action: 'copy' }, |
| | | { |
| | | label: '移动到', |
| | | children: this.allMoveActions.filter((v) => { |
| | | return v.value != this.selectedTypeId; |
| | | }) |
| | | } |
| | | }, |
| | | deep: true |
| | | }, |
| | | typeImgMap: { |
| | | handler(newMap, oldMap) { |
| | | if (this.isClose || newMap.get(this.activeId) == undefined) { |
| | | return; |
| | | } |
| | | newMap.get(this.activeId).forEach((i) => { |
| | | if (i.isSelect == true) { |
| | | return; |
| | | } |
| | | this.defaultFile.forEach((imgItem) => { |
| | | if (imgItem.url == i.url) { |
| | | i.isSelect = true; |
| | | } |
| | | }); |
| | | }); |
| | | }, |
| | | |
| | | deep: true |
| | | ]; |
| | | } |
| | | }, |
| | | mounted() { |
| | | if (this.defaultFile == undefined || this.defaultFile == null) { |
| | | this.defaultFile = []; |
| | | } |
| | | if (this.subtask) { |
| | | this.getAllImgList(); |
| | | } |
| | | this.getGroupImgs(); |
| | | this.closeContextMenuListenr = (event) => { |
| | | if ( |
| | | this.$refs.menu && |
| | | !this.$refs.menu.contains(event.target) && |
| | | this.showMenu |
| | | ) { |
| | | // 如果点击不是在菜单内部且菜单是可见的 |
| | | this.showMenu = false; // 隐藏菜单 |
| | | } |
| | | }; |
| | | document.addEventListener('click', this.closeContextMenuListenr); |
| | | |
| | | this.initMenuItems(); |
| | | }, |
| | | unmounted() { |
| | | document.removeEventListener('click', this.closeContextMenuListenr); |
| | | }, |
| | | methods: { |
| | | // 初始化刚开始选中的标签 |
| | | initSelectedTab() { |
| | | if (this.typeList.length > 0) { |
| | | this.activeId = this.typeList[0].businesstypeid; |
| | | // 图片分类 |
| | | getGroupImgs() { |
| | | mediafileApi |
| | | .getRoutineByStGuid(this.subtask.stGuid) |
| | | .then((res) => { |
| | | this.loading = true; |
| | | let typeList = []; |
| | | let typeMap = new Map(); |
| | | const data = res.data; |
| | | for (const e of data) { |
| | | let img = { |
| | | url: $fysp.imgUrl + e.extension1 + e.guid + '.jpg', |
| | | data: e |
| | | }; |
| | | const businesstype = e.businesstype; |
| | | const businesstypeid = e.businesstypeid; |
| | | if ( |
| | | typeList.find((item) => item.typeId == businesstypeid) != |
| | | undefined |
| | | ) { |
| | | typeMap.get(businesstypeid).push(img); |
| | | } else { |
| | | typeList.push({ |
| | | typeId: businesstypeid, |
| | | typeName: businesstype |
| | | }); |
| | | typeMap.set(businesstypeid, [img]); |
| | | } |
| | | } |
| | | this.typesList = typeList; |
| | | this.typesMap = typeMap; |
| | | }) |
| | | .finally(() => (this.loading = false)); |
| | | }, |
| | | // 图片右键点击事件 |
| | | showContextMenu(event, typeId, index) { |
| | | this.showMenu = true; |
| | | this.menuStyle = { |
| | | left: `${event.clientX}px`, |
| | | top: `${event.clientY}px` |
| | | }; |
| | | this.selectedFileElement = event.target; |
| | | this.selectedTypeId = typeId; |
| | | this.selectedIndex = index; |
| | | this.selectedFile = this.typesMap.get(typeId)[index]; |
| | | }, |
| | | closeContextMenu() { |
| | | this.showMenu = false; |
| | | }, |
| | | handleMenuItem(item) { |
| | | switch (item.action) { |
| | | case 'copy': |
| | | this.drawImageToCanvas(); |
| | | break; |
| | | case 'move': |
| | | this.selectedFile.data.businesstypeid = item.value; |
| | | this.selectedFile.data.businesstype = item.label; |
| | | this.changeMediaFileType(this.selectedFile); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | this.closeContextMenu(); |
| | | }, |
| | | async getAllImgList() { |
| | | // for(var k of this.typeImgMap.keys()) { |
| | | // this.typeImgMap.set(k, []) |
| | | // } |
| | | await mediafileApi.getRoutineByStGuid(this.subtask.stGuid).then((res) => { |
| | | this.isEmpty = false; |
| | | let data = res.data; |
| | | if (data.length == 0) { |
| | | this.isEmpty = true; |
| | | } |
| | | for (const e of data) { |
| | | let list; |
| | | let businesstypeid = e.businesstypeid; |
| | | let businesstype = e.businesstype; |
| | | let hasThisType = false; |
| | | this.typeImgMap.forEach((v, k, m) => { |
| | | if (k == businesstypeid) { |
| | | hasThisType = true; |
| | | var isAlreadyHas = false; |
| | | if (v != undefined && v != null) { |
| | | for (let index = 0; index < v.length; index++) { |
| | | const element = v[index]; |
| | | if (element.guid == e.guid) { |
| | | isAlreadyHas = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if (!isAlreadyHas) { |
| | | v.push(e); |
| | | } |
| | | changeMediaFileType() { |
| | | this.selectedFile.loading = true; |
| | | const file = this.selectedFile.data; |
| | | return mediafileApi |
| | | .updateMediaFile(file) |
| | | .then((res) => { |
| | | if (res == 1) { |
| | | const m = this.typesMap |
| | | .get(this.selectedTypeId) |
| | | .splice(this.selectedIndex, 1); |
| | | if (!this.typesMap.has(file.businesstypeid)) { |
| | | this.typesList.push({ |
| | | typeId: file.businesstypeid, |
| | | typeName: file.businesstype |
| | | }); |
| | | this.typesMap.set(file.businesstypeid, m); |
| | | } else { |
| | | this.typesMap.get(file.businesstypeid).push(m[0]); |
| | | } |
| | | }); |
| | | if (!hasThisType) { |
| | | this.typeImgMap.set(businesstypeid, Array.of(e)); |
| | | this.typeList.push(e); |
| | | } |
| | | this.imgUrlList.push(e); |
| | | // TODO imgUrl全局配置 |
| | | e.url = $fysp.imgUrl + e.extension1 + e.guid + '.jpg'; |
| | | // e.url = "http://47.100.191.150:9005/images/" + e.extension1 + e.guid + '.jpg' |
| | | e.isSelect = false; |
| | | } |
| | | this.initSelectedTab(); |
| | | }); |
| | | }, |
| | | getInitImgList() { |
| | | mediafileApi.getRoutineByStGuid(this.subtask.stGuid).then((res) => { |
| | | let data = res.data; |
| | | for (const e of data) { |
| | | let list; |
| | | let businesstypeid = e.businesstypeid; |
| | | let businesstype = e.businesstype; |
| | | let hasThisType = false; |
| | | this.typeImgMap.forEach((v, k, m) => { |
| | | if (k == businesstypeid) { |
| | | hasThisType = true; |
| | | if (v.length < 3) { |
| | | v.push(e); |
| | | } |
| | | } |
| | | }); |
| | | if (!hasThisType) { |
| | | this.typeImgMap.set(businesstypeid, Array.of(e)); |
| | | this.typeList.push(e); |
| | | } |
| | | this.imgUrlList.push(e); |
| | | // TODO imgUrl全局配置 |
| | | e.url = $fysp.imgUrl + e.extension1 + e.guid + '.jpg'; |
| | | // e.url = "http://47.100.191.150:9005/images/" + e.extension1 + e.guid + '.jpg' |
| | | e.isSelect = false; |
| | | } |
| | | if (this.typeList.length > 0) { |
| | | this.activeId = this.typeList[0].businesstypeid; |
| | | } |
| | | }); |
| | | }, |
| | | onSelect(img, i) { |
| | | // if (i == 2 && !this.isAll) { |
| | | // this.getAllImgList(); |
| | | // this.isAll = true; |
| | | // } else { |
| | | // if (this.readonly) { |
| | | // return; |
| | | // } |
| | | // img.isSelect = !Boolean(img.isSelect); |
| | | // } |
| | | }) |
| | | .finally(() => (this.selectedFile.loading = false)); |
| | | |
| | | if (this.readonly) { |
| | | return; |
| | | } |
| | | img.isSelect = !Boolean(img.isSelect); |
| | | // setTimeout(() => { |
| | | // const m = this.typesMap |
| | | // .get(this.selectedTypeId) |
| | | // .splice(this.selectedIndex, 1); |
| | | // if (!this.typesMap.has(file.businesstypeid)) { |
| | | // this.typesList.push({ |
| | | // typeId: file.businesstypeid, |
| | | // typeName: file.businesstype |
| | | // }); |
| | | // this.typesMap.set(file.businesstypeid, m); |
| | | // } else { |
| | | // this.typesMap.get(file.businesstypeid).push(m[0]); |
| | | // } |
| | | // this.selectedFile.loading = false; |
| | | // }, 2000); |
| | | }, |
| | | sendSelectedImg(isOk) { |
| | | let result = []; |
| | | if (!Boolean(isOk)) { |
| | | this.$emit('selectByAnyPhonoEvent', result); |
| | | } |
| | | for (const item of this.imgUrlList) { |
| | | if (item.isSelect == true) { |
| | | result.push(item); |
| | | } |
| | | } |
| | | this.isClose = true; |
| | | this.$emit('selectByAnyPhonoEvent', result); |
| | | drawImageToCanvas() { |
| | | const canvas = document.createElement('canvas'); |
| | | const ctx = canvas.getContext('2d'); |
| | | const img = this.selectedFileElement; // 获取DOM引用 |
| | | // img.crossOrigin = 'Anonymous'; |
| | | canvas.width = img.naturalWidth; |
| | | canvas.height = img.naturalHeight; |
| | | ctx.drawImage(img, 0, 0, canvas.width, canvas.height); |
| | | this.copyCanvasToClipboard(canvas); |
| | | const dataUrl = canvas.toDataURL('image/png'); // 可以选择其他格式如'image/jpeg' |
| | | // 创建一个临时的textarea元素来复制文本 |
| | | const tempTextArea = document.createElement('textarea'); |
| | | document.body.appendChild(tempTextArea); |
| | | tempTextArea.value = dataUrl; |
| | | tempTextArea.select(); |
| | | document.execCommand('copy'); |
| | | document.body.removeChild(tempTextArea); |
| | | }, |
| | | copyCanvasToClipboard(canvas) { |
| | | canvas.toBlob((blob) => { |
| | | const item = new ClipboardItem({ 'image/png': blob }); // 你可以根据需要改变格式,如 "image/jpeg" |
| | | navigator.clipboard.write([item]).then( |
| | | () => { |
| | | console.log('Image copied to clipboard'); |
| | | }, |
| | | (err) => { |
| | | console.error('Could not copy image: ', err); |
| | | } |
| | | ); |
| | | }, 'image/png'); // 同样,这里也可以指定其他格式,如 'image/jpeg' |
| | | }, |
| | | // 初始化场景图片的类型菜单 |
| | | initMenuItems() { |
| | | this.menuLoading = true; |
| | | const sceneTypeId = this.subtask.sceneTypeId; |
| | | enumMediaFile(sceneTypeId, false) |
| | | .then((res) => { |
| | | this.allMoveActions = res.map((v) => { |
| | | return { |
| | | action: 'move', |
| | | label: v.label, |
| | | value: v.value |
| | | }; |
| | | }); |
| | | }) |
| | | .finally(() => (this.menuLoading = false)); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | <style scoped> |
| | | .center { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | .container { |
| | | /* background-color: rgba(19, 211, 67, 0.514); */ |
| | | position: fixed; |
| | | left: 0; |
| | | top: 0; |
| | | height: 100vh; |
| | | width: 100vw; |
| | | z-index: 10000; |
| | | } |
| | | .text { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .main { |
| | | margin: 0 auto; /* 使父元素居中 */ |
| | | height: 100%; |
| | | width: 100%; |
| | | } |
| | | |
| | | .btns { |
| | | height: 10%; |
| | | } |
| | | /* |
| | | .img_types { |
| | | margin: 0 auto; |
| | | height: 440px; |
| | | width: 900px; |
| | | flex-grow: 1; |
| | | overflow-y: hidden ; |
| | | padding: 3%; |
| | | flex-wrap: wrap; |
| | | overflow: hidden; |
| | | } */ |
| | | |
| | | .imgs { |
| | | height: 370px; |
| | | width: 90%; |
| | | min-height: 100px !important; |
| | | /* border-style:solid; |
| | | border-radius: 1px; */ |
| | | /* height: 100%; */ |
| | | flex-grow: 1 !important; |
| | | overflow-y: auto !important; |
| | | /* 内容的内边距 */ |
| | | display: flex !important; |
| | | flex-wrap: wrap !important; |
| | | /* overflow: hidden; */ |
| | | } |
| | | |
| | | .image { |
| | | height: 210px; |
| | | width: 200px; |
| | | .context-menu { |
| | | position: fixed; |
| | | background: white; |
| | | border-radius: 4px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
| | | min-width: 200px; |
| | | z-index: 10001; |
| | | } |
| | | |
| | | .active { |
| | | padding: 5px; |
| | | width: 20%; |
| | | height: 200px; |
| | | border: 0.5rem outset rgb(52, 155, 4); |
| | | .menu-item { |
| | | padding: 8px 16px; |
| | | cursor: pointer; |
| | | transition: background 0.2s; |
| | | } |
| | | |
| | | .selected { |
| | | padding: 5px; |
| | | color: #4abe84; |
| | | box-shadow: 0 2px 7px 0 rgba(85, 110, 97, 0.35); |
| | | border: 1px solid rgba(74, 190, 132, 1); |
| | | } |
| | | |
| | | .selected:before { |
| | | content: ''; |
| | | position: absolute; |
| | | right: 0; |
| | | bottom: 0; |
| | | border: 17px solid #4abe84; |
| | | border-top-color: transparent; |
| | | border-left-color: transparent; |
| | | } |
| | | |
| | | .selected:after { |
| | | content: ''; |
| | | width: 5px; |
| | | height: 12px; |
| | | position: absolute; |
| | | right: 6px; |
| | | bottom: 6px; |
| | | border: 2px solid #fff; |
| | | border-top-color: transparent; |
| | | border-left-color: transparent; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | .noActive { |
| | | padding: 5px; |
| | | } |
| | | |
| | | .blurry { |
| | | filter: blur(3px); |
| | | } |
| | | .filters { |
| | | display: flex; |
| | | padding: 5px; |
| | | } |
| | | |
| | | .el-dialog__body { |
| | | height: 60vh; |
| | | .menu-item:hover { |
| | | background-color: #f0f0f0; |
| | | } |
| | | </style> |