EDBClassify.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. <script setup>
  2. import {computed, reactive, ref,watch} from 'vue'
  3. import { useWindowSize } from '@vueuse/core'
  4. import apiDataEDB from '@/api/dataEDB'
  5. import { showDialog , showToast } from 'vant';
  6. import {useEDBDelete} from '../hooks/useEDBDelete'
  7. import {deleteClassifyItemEmpty} from '../util/util'
  8. import {edbDataBtn,useAuthBtn} from '@/hooks/useAuthBtn'
  9. const {checkAuthBtn} = useAuthBtn()
  10. const {edbClassifyDelete}=useEDBDelete()
  11. const { width } = useWindowSize()
  12. const props=defineProps({
  13. modelValue:{
  14. type:Boolean,
  15. default:false
  16. }
  17. })
  18. const emits=defineEmits(['update:modelValue','change'])
  19. // 处理分类数据 index为要过滤的层级数
  20. // 如index 为3 则获取arr的前三层数据
  21. function formatClassifyArr(arr,index){
  22. index--
  23. arr.length&&arr.forEach(item=>{
  24. if(!item.Children?.length||index<=0){
  25. delete item.Children
  26. }
  27. item.Children?.length&&formatClassifyArr(item.Children,index)
  28. })
  29. }
  30. const langType=ref('zh')
  31. function langTypeChange(type){
  32. langType.value=type
  33. }
  34. const selectClassifyArr=ref([])//当前选择的哪些目录
  35. function findClassifyList(arr,id){
  36. let temarr=[]
  37. arr.forEach(item=>{
  38. if(item.ClassifyId===id){
  39. if(item.Children?.length){
  40. temarr=item.Children
  41. }else{// 如果选中了最后一级目录或者这个目录没有子目录则就返回他本身
  42. temarr=arr
  43. }
  44. }
  45. })
  46. return temarr
  47. }
  48. //显示的目录数据
  49. const showClassifyArrData=computed(()=>{
  50. if(selectClassifyArr.value.length===0) return classifyList.value
  51. let arr=classifyList.value
  52. for (let index = 0; index < selectClassifyArr.value.length; index++) {
  53. arr=findClassifyList(arr,selectClassifyArr.value[index].ClassifyId)
  54. }
  55. return arr
  56. })
  57. //点击目录
  58. function handleSelectClassify(item){
  59. // 如果当前选择的分类的最后一个在当前这个显示的目录里则将最后一个替换掉
  60. const last=selectClassifyArr.value[selectClassifyArr.value.length-1]
  61. // console.log(last);
  62. const flag=showClassifyArrData.value.some(e=>e.ClassifyId===last?.ClassifyId)
  63. if(flag){
  64. selectClassifyArr.value[selectClassifyArr.value.length-1]=item
  65. return
  66. }
  67. if(item.ClassifyId!==selectClassifyArr.value[selectClassifyArr.value.length-1]?.ClassifyId){
  68. selectClassifyArr.value.push(item)
  69. }
  70. }
  71. //返回上级目录
  72. function handleBackClassify(index){
  73. if(index===-1){
  74. const flag=showClassifyArrData.value.some(item=>item.ClassifyId===selectClassifyArr.value[selectClassifyArr.value.length-1]?.ClassifyId)
  75. if(flag){
  76. selectClassifyArr.value.length=selectClassifyArr.value.length-2
  77. }else{
  78. selectClassifyArr.value.length=selectClassifyArr.value.length-1
  79. }
  80. return
  81. }
  82. selectClassifyArr.value.length=selectClassifyArr.value.length-index
  83. }
  84. // 显示选择的父级目录
  85. const showParentClassifyData=computed(()=>{
  86. const id=selectClassifyArr.value[selectClassifyArr.value.length-1]?.ClassifyId
  87. const flag=showClassifyArrData.value.some(item=>item.ClassifyId===id)
  88. if(flag) return selectClassifyArr.value.slice(0,selectClassifyArr.value.length-1)
  89. return selectClassifyArr.value
  90. })
  91. // 关闭弹窗
  92. function handleClose(){
  93. let selectClassifyId=selectClassifyArr.value[selectClassifyArr.value.length-1]?.ClassifyId||0
  94. let selectClassifyNameArr=selectClassifyArr.value.map(item=>getClassifyItemName(item))
  95. emits('change',{selectClassifyNameStr:selectClassifyNameArr.join('/'),selectClassifyId})
  96. emits('update:modelValue',false)
  97. }
  98. // 设置显示的分类名称(中英文)
  99. function getClassifyItemName(item){
  100. return langType.value==='zh'?item.ClassifyName:item.ClassifyNameEn||item.ClassifyName
  101. }
  102. // 获取分类数据classifyState
  103. let classifyList=ref([])
  104. async function getEDBClassifyList(){
  105. const res=await apiDataEDB.edbClassifyList()
  106. if(res.Ret===200){
  107. deleteClassifyItemEmpty(res.Data.AllNodes||[])
  108. classifyList.value=res.Data.AllNodes||[]
  109. }
  110. }
  111. getEDBClassifyList()
  112. // 分类操作数据
  113. const classifyState=reactive({
  114. show:false,
  115. title:'',
  116. name:'',//输入框中的值
  117. placeholderText:'',
  118. type:'add',//新增、编辑
  119. parentText:'',
  120. parentId:0,
  121. classifyId:0,
  122. })
  123. watch(
  124. ()=>classifyState.show,
  125. (n)=>{
  126. if(!n){
  127. classifyState.title=''
  128. classifyState.name=''
  129. classifyState.placeholderText=''
  130. classifyState.type='add'
  131. classifyState.parentText=''
  132. classifyState.parentId=0
  133. classifyState.classifyId=0
  134. }
  135. }
  136. )
  137. function handleAddLevel1(){
  138. classifyState.title='添加一级目录'
  139. classifyState.placeholderText='请输入一级目录名称'
  140. classifyState.show=true
  141. }
  142. async function handleConfirmClassifyState(){
  143. let params={}
  144. if(classifyState.type==='add'){
  145. params={
  146. ClassifyName:classifyState.name,
  147. Level:classifyState.level,
  148. ParentId:classifyState.parentId
  149. }
  150. }else{
  151. params={
  152. ClassifyName:classifyState.name,
  153. ClassifyId:classifyState.classifyId
  154. }
  155. }
  156. const res=classifyState.type==='add'?await apiDataEDB.edbClassifyAdd(params):await apiDataEDB.edbClassifyEdit(params)
  157. if(res.Ret===200){
  158. getEDBClassifyList()
  159. classifyState.show=false
  160. }
  161. }
  162. // 显示分类操作按钮数据
  163. const classifyOptState=reactive({
  164. show:false,
  165. actions:[],
  166. data:{}
  167. })
  168. watch(
  169. ()=>classifyOptState.show,
  170. (n)=>{
  171. if(!n){
  172. classifyOptState.actions=[]
  173. classifyOptState.data={}
  174. }
  175. }
  176. )
  177. //判断每一项的optArr并返回长度
  178. function checkOpt(item){
  179. let optArr = []
  180. if(item.Button.AddButton&&checkAuthBtn(edbDataBtn.edbData_classifyOpt_add)&&item.Level<6){
  181. optArr.push({name:'添加下级目录',type:'add'})
  182. }
  183. if(checkAuthBtn(edbDataBtn.edbData_classifyOpt_add)){
  184. optArr.push({name:'重命名',type:'edit'})
  185. }
  186. if(item.Button.DeleteButton&&checkAuthBtn(edbDataBtn.edbData_classifyOpt_delete)){
  187. optArr.push({name:'删除',type:'delete',color:"#C54322"})
  188. }
  189. if(item.Level!==1&&item.Button.MoveButton&&checkAuthBtn(edbDataBtn.edbData_classifyOpt_move)){
  190. optArr.push({name:'移动至',type:'move'})
  191. }
  192. return optArr.length
  193. }
  194. // 显示分类的具体操作选项
  195. function handleShowClassifyOpt(item){
  196. let optArr=[
  197. {name:'重命名',type:'edit'},
  198. {name:'删除',type:'delete',color:"#C54322"},
  199. ]
  200. if(item.Button.AddButton&&showParentClassifyData.value.length<5){//添加权限
  201. checkAuthBtn(edbDataBtn.edbData_classifyOpt_add)&&optArr.unshift({name:'添加下级目录',type:'add'})
  202. }
  203. if(!checkAuthBtn(edbDataBtn.edbData_classifyOpt_add)){//重命名
  204. optArr=optArr.filter(item=>item.type!='edit')
  205. }
  206. if(!item.Button.DeleteButton||!checkAuthBtn(edbDataBtn.edbData_classifyOpt_delete)){//删除
  207. optArr=optArr.filter(item=>item.type!='delete')
  208. }
  209. if(item.Button.MoveButton&&checkAuthBtn(edbDataBtn.edbData_classifyOpt_move)){//移动权限
  210. // 一级目录不能移动至
  211. if(selectClassifyArr.value.length){
  212. optArr.push({name:'移动至',type:'move'})
  213. }
  214. }
  215. classifyOptState.actions=optArr
  216. classifyOptState.data=item
  217. optArr.length&&(classifyOptState.show=true)
  218. }
  219. function handleSelectOpt(e){
  220. if(e.type==='add'){
  221. const temarr=[...showParentClassifyData.value,{...classifyOptState.data}]
  222. const selectClassifyNameArr=temarr.map(item=>getClassifyItemName(item))
  223. classifyState.parentText=selectClassifyNameArr.join('/')
  224. classifyState.title=e.name
  225. classifyState.placeholderText='请输入目录名称'
  226. classifyState.type='add'
  227. classifyState.parentId=classifyOptState.data.ClassifyId
  228. classifyState.show=true
  229. }
  230. if(e.type==='edit'){
  231. const temarr=[...showParentClassifyData.value]
  232. const selectClassifyNameArr=temarr.map(item=>getClassifyItemName(item))
  233. classifyState.parentText=selectClassifyNameArr.join('/')
  234. classifyState.title='重命名'
  235. classifyState.placeholderText='请输入目录名称'
  236. classifyState.type='edit'
  237. classifyState.name=classifyOptState.data.ClassifyName
  238. classifyState.classifyId=classifyOptState.data.ClassifyId
  239. classifyState.show=true
  240. }
  241. if(e.type==='delete'){
  242. let data=classifyOptState.data
  243. edbClassifyDelete(data).then(res=>{
  244. // console.log(res===true);
  245. if(res===true){
  246. getEDBClassifyList()
  247. }
  248. })
  249. }
  250. if(e.type==='move'){
  251. classifyMoveState.selectValue=''
  252. classifyMoveState.tabIndex=0
  253. const level=showParentClassifyData.value.length
  254. const arr=JSON.parse(JSON.stringify(classifyList.value))
  255. formatClassifyArr(arr,level)
  256. classifyMoveState.options=arr||[]
  257. classifyMoveState.classifyId=classifyOptState.data.ClassifyId
  258. classifyMoveState.show=true
  259. }
  260. classifyOptState.show=false
  261. }
  262. // 移动分类数据
  263. const classifyMoveState=reactive({
  264. show:false,
  265. selectValue:'',
  266. options:[],
  267. classifyId:0,//要移动的分类id
  268. tabIndex:0,
  269. })
  270. function handleSelectMoveClassifyChange({value,selectedOptions,tabIndex}){
  271. classifyMoveState.tabIndex=tabIndex
  272. }
  273. function handleConfirmMove(){
  274. if(!classifyMoveState.selectValue){
  275. showToast('请选择要移动至的目录')
  276. return
  277. }
  278. // 目录移动需保证其层级不变(二级目录只能移动到其他的一级目录下)
  279. if(classifyMoveState.tabIndex+1<showParentClassifyData.value.length){
  280. showToast('不可移动至该目录下')
  281. return
  282. }
  283. // 只会将目录移动到某个目录里面所有下面有很多都是0
  284. apiDataEDB.edbCatalogMove({
  285. ClassifyId:classifyMoveState.classifyId,
  286. ParentClassifyId:classifyMoveState.selectValue,
  287. EdbInfoId:0,
  288. PrevClassifyId:0,
  289. NextClassifyId:0,
  290. PrevEdbInfoId:0,
  291. NextEdbInfoId:0,
  292. }).then(res=>{
  293. if(res.Ret===200){
  294. showToast('移动成功')
  295. getEDBClassifyList()
  296. classifyMoveState.show=false
  297. }
  298. })
  299. }
  300. defineExpose({classifyList,langTypeChange})
  301. </script>
  302. <template>
  303. <van-popup
  304. :show="props.modelValue"
  305. position="right"
  306. closeable
  307. :style="{width:width>650?'400px':'100%',height:'100%'}"
  308. @click-close-icon="handleClose"
  309. >
  310. <div class="edb-classify-wrap">
  311. <div class="top-box">
  312. <van-icon name="arrow-left" size='20' @click="handleBackClassify(-1)" v-if="showParentClassifyData.length>0"/>
  313. <div class="catalog-text-box">
  314. <span
  315. v-for="item,index in showParentClassifyData"
  316. :key="item.ClassifyId"
  317. @click="handleBackClassify(selectClassifyArr.length-index)"
  318. >{{getClassifyItemName(item)}}</span>
  319. </div>
  320. </div>
  321. <ul class="classify-box">
  322. <li class="classify-item" v-for="item in showClassifyArrData" :key="item.UniqueCode">
  323. <span
  324. class="van-ellipsis title"
  325. :style="{color:selectClassifyArr[selectClassifyArr.length-1]?.ClassifyId===item.ClassifyId?'#0052D9':''}"
  326. @click.stop="handleSelectClassify(item)"
  327. >{{getClassifyItemName(item)}}</span>
  328. <img style="width:22px;height:22px;cursor: pointer;" src="@/assets/imgs/dataEDB/icon_opt.png" alt=""
  329. v-if="checkOpt(item)"
  330. @click.stop="handleShowClassifyOpt(item)">
  331. </li>
  332. </ul>
  333. <div class="bot-btn-box" v-permission="edbDataBtn.edbData_classifyOpt_add">
  334. <van-button type="primary" block @click="handleAddLevel1">添加一级目录</van-button>
  335. </div>
  336. </div>
  337. </van-popup>
  338. <!-- 分类操作选项 -->
  339. <van-action-sheet
  340. v-model:show="classifyOptState.show"
  341. :actions="classifyOptState.actions"
  342. cancel-text="取消"
  343. @select="handleSelectOpt"
  344. />
  345. <!-- 创建/编辑分类 -->
  346. <van-dialog
  347. v-model:show="classifyState.show"
  348. :title="classifyState.title"
  349. show-cancel-button
  350. @confirm="handleConfirmClassifyState"
  351. >
  352. <div class="edit-classify-wrap">
  353. <p class="van-ellipsis" v-if="classifyState.parentText" style="margin-bottom:8px">上级目录:{{classifyState.parentText}}</p>
  354. <input class="input-box" type="text" :placeholder="classifyState.placeholderText" v-model="classifyState.name">
  355. </div>
  356. </van-dialog>
  357. <!-- 移动分类 -->
  358. <van-popup
  359. v-model:show="classifyMoveState.show"
  360. round
  361. position="bottom"
  362. >
  363. <van-cascader
  364. v-model="classifyMoveState.selectValue"
  365. title="移动至"
  366. :options="classifyMoveState.options"
  367. :field-names="{text:'ClassifyName',value:'ClassifyId',children:'Children'}"
  368. @close="classifyMoveState.show = false"
  369. @change="handleSelectMoveClassifyChange"
  370. />
  371. <div style="width:300px;margin:0 auto;padding:20px 0">
  372. <van-button type="primary" round block @click="handleConfirmMove">确定</van-button>
  373. </div>
  374. </van-popup>
  375. </template>
  376. <style lang="scss" scoped>
  377. .edb-classify-wrap{
  378. height: 100%;
  379. display: flex;
  380. flex-direction: column;
  381. .top-box{
  382. height: calc(var(--van-popup-close-icon-margin) + 50px);
  383. background-color: #fff;
  384. flex-shrink: 0;
  385. display: flex;
  386. align-items: center;
  387. padding-right: calc(var(--van-popup-close-icon-margin) + var(--van-popup-close-icon-size));
  388. padding-top: var(--van-popup-close-icon-margin);
  389. padding-left: 28px;
  390. .catalog-text-box{
  391. margin-left: 20px;
  392. flex: 1;
  393. overflow-x: auto;
  394. display: flex;
  395. &::-webkit-scrollbar{
  396. display: none;
  397. height: 0;
  398. }
  399. span{
  400. display: block;
  401. flex-shrink: 0;
  402. font-size: 32px;
  403. font-weight: 600;
  404. &::after{
  405. content:'/'
  406. }
  407. &:last-child::after{
  408. content:''
  409. }
  410. }
  411. }
  412. }
  413. .classify-box{
  414. flex: 1;
  415. overflow-y: auto;
  416. padding: 0 32px;
  417. .classify-item{
  418. display: flex;
  419. align-items: center;
  420. padding: 10px 0;
  421. .title{
  422. flex: 1;
  423. display: block;
  424. }
  425. }
  426. }
  427. .bot-btn-box{
  428. padding: 48px;
  429. flex-shrink: 0;
  430. }
  431. }
  432. .edit-classify-wrap{
  433. padding: 32px 48px;
  434. .input-box{
  435. width: 100%;
  436. box-sizing: border-box;
  437. display: block;
  438. padding: 24px 32px;
  439. background-color: #f6f6f6;
  440. border-radius: 12px;
  441. }
  442. }
  443. @media screen and (min-width:$media-width){
  444. .edb-classify-wrap{
  445. .top-box{
  446. height: calc(var(--van-popup-close-icon-margin) + 50px);
  447. padding-left: 14px;
  448. .catalog-text-box{
  449. margin-left: 10px;
  450. span{
  451. font-size: 16px;
  452. }
  453. }
  454. }
  455. .classify-box{
  456. padding: 0 16px;
  457. .classify-item{
  458. padding: 5px 0;
  459. }
  460. }
  461. .bot-btn-box{
  462. padding: 24px;
  463. }
  464. }
  465. .edit-classify-wrap{
  466. padding: 16px 24px;
  467. .input-box{
  468. padding: 12px 16px;
  469. border-radius: 6px;
  470. }
  471. }
  472. }
  473. </style>