showing.vue 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. <script>
  2. import axios from "axios";
  3. import {defineComponent} from "vue";
  4. import fieldIsAllow from "../../../until/fieldIsAllow"
  5. import RoundedTitle from "../../../components/public/roundedTitle.vue";
  6. import {rCode} from "../../../map/rcodeMap_esm";
  7. import {handle} from "../../../until/handle";
  8. import ImageViewer from "../../../components/public/imageViewer.vue";
  9. import ImageTable from "../../../components/public/imageTable.vue";
  10. import Pop from "../../../components/public/pop.vue";
  11. import PopCard from "../../../components/public/popCard.vue";
  12. import InputRow from "../../../components/public/form/inputRow.vue";
  13. import dbField_esm from "../../../map/dbField_esm";
  14. import {apiMap} from "../../../map/apiMap";
  15. import SearchBox from "../../../components/search/searchBox.vue";
  16. import {pTypes} from "../../../map/productMap";
  17. import {newsType} from "../../../map/newMap";
  18. import {toNumber,isEmpty} from "../../../until/typeTool";
  19. export default defineComponent({
  20. name: 'showing',
  21. computed: {
  22. dbField_esm() {
  23. return dbField_esm
  24. },
  25. productTypes(){
  26. let arr = this.$store.getters.productTypes;
  27. // 添加 all
  28. arr.unshift({text: '全部', key: 'all' });
  29. return arr;
  30. },
  31. newsTypes(){// 添加 all
  32. let arr = this.$store.getters.allNewsTypes;
  33. arr.unshift({text: '全部', key: 'all' });
  34. return arr;
  35. },
  36. },
  37. components: {SearchBox, InputRow, PopCard, Pop, ImageTable, ImageViewer, RoundedTitle},
  38. async asyncData(ctx){
  39. // 加载展示块数据
  40. let [err,res] = await handle(axios.get(apiMap.showBlocksList.path));
  41. if(err){
  42. return {};
  43. }
  44. let result = res.data;
  45. if(result.code === rCode.OK){
  46. return {showBlockList: result.data}
  47. }else{
  48. this.$message.error(result.msg);
  49. return {}
  50. }
  51. return {}
  52. },
  53. data(){
  54. return {
  55. limit: 10,
  56. loading: false,
  57. showBlockList: [],
  58. popShow: false,
  59. popLoading: false,
  60. showBlockPopShow: false,
  61. showBlockPopLoading: false,
  62. isEditShowBlock: false,
  63. showBlockData: {},
  64. form: {
  65. // 排序
  66. sort: {
  67. val:0,
  68. init: 0,
  69. msg: '',
  70. state: 0,
  71. },
  72. // 状态 0:禁用,1:启用
  73. state: {
  74. val: dbField_esm.db_base.carouselState.disable,
  75. init: dbField_esm.db_base.carouselState.disable,
  76. msg: '',
  77. state: 0,
  78. options: [
  79. {label: '禁用', value: dbField_esm.db_base.carouselState.disable},
  80. {label: '启用', value: dbField_esm.db_base.carouselState.enable},
  81. ]
  82. },
  83. // 展示块类型 '0:product','1:news','2:page','3:href'
  84. type: {
  85. val: 0,
  86. init: 0,
  87. msg: '',
  88. state: 0,
  89. options: [
  90. {label: '直接链接', value: 0,checkField: 'href'},
  91. {label: '内部产品', value: 1,checkField: 'productId'},
  92. {label: '指向新闻', value: 2,checkField: 'newsId'},
  93. ],
  94. },
  95. // 具体值
  96. value: {
  97. val: '',
  98. init: '',
  99. msg: '',
  100. state: 0,
  101. // 依赖字段
  102. depend: 'type',
  103. showText: '',// 展示用字段
  104. oldShowText: '',
  105. },
  106. // 标题
  107. title: {
  108. val: '',
  109. init: '',
  110. msg: '',
  111. state: 0,
  112. },
  113. // 副标题
  114. subTitle: {
  115. val: '',
  116. init: '',
  117. msg: '',
  118. state: 0,
  119. },
  120. // file
  121. fileData: {
  122. val: '',
  123. init: '',
  124. msg: '',
  125. state: 0,
  126. showText: '',// 展示用字段
  127. oldShowText: '',
  128. },
  129. },
  130. productSelectVisible: false,
  131. productSearch: {
  132. type: {
  133. val: '',
  134. oldVal: '',
  135. init: '',
  136. msg: '',
  137. options: [],
  138. }
  139. },
  140. newsSelectVisible: false,
  141. newsVisible: false,
  142. newsSearch: {
  143. type: {
  144. val: '',
  145. oldVal: '',
  146. init: '',
  147. msg: '',
  148. options: [],
  149. }
  150. },
  151. imageSelectVisible: false,
  152. }
  153. },
  154. mounted() {
  155. if(this.showBlockList.length === 0){
  156. this.getShowBlockList();
  157. }
  158. },
  159. methods: {
  160. async getShowBlockList(){
  161. this.loading = true;
  162. let [err,res] = await handle(this.$axios.get(apiMap.showBlocksList.path));
  163. this.loading = false;
  164. if(err){
  165. if(this.NotificationKey){
  166. this.$notification.close(this.NotificationKey);
  167. }
  168. this.NotificationKey = `open${Date.now()}`;
  169. return this.$notification.error({
  170. message: '展示块数据加载失败',
  171. description:`异常: ${err.message}`,
  172. duration: 0,
  173. btn: h => {
  174. return h(
  175. 'a-button',
  176. {
  177. props: {
  178. type: 'primary',
  179. size: 'small',
  180. },
  181. on: {
  182. click: () => {
  183. this.$notification.close(this.NotificationKey);
  184. this.getShowBlockList();
  185. },
  186. },
  187. },
  188. '重试',
  189. );
  190. },
  191. key:this.NotificationKey,
  192. onClose: close,
  193. });
  194. }
  195. let result = res.data;
  196. if(result.code === rCode.OK){
  197. this.showBlockList = result.data;
  198. return {showBlockList: result.data}
  199. }else{
  200. this.$message.error(result.msg);
  201. return {}
  202. }
  203. },
  204. // 搜索产品,只需要产品名等信息
  205. async getProductSearch(searchParam){
  206. console.log(searchParam)
  207. if(this.productSearch.type.val !== this.productSearch.type.oldVal){
  208. searchParam.p = 1;
  209. }
  210. searchParam.type = this.productSearch.type.val;
  211. searchParam.l = this.limit;
  212. searchParam.p = searchParam.page;
  213. let [err,res] = await handle(
  214. this.$axios.get(
  215. apiMap.searchProductMini.path,
  216. {params:searchParam})
  217. );
  218. if(err){
  219. console.log(err);
  220. return [{message:'请求数据失败'},null];
  221. }
  222. let result = res.data;
  223. if(result.code === rCode.OK){
  224. this.productSearch.type.oldVal = this.productSearch.type.val;
  225. // data 转换
  226. result.data = result.data.map(item=>{
  227. item.showText = item.name;
  228. return item;
  229. });
  230. return [null,result];
  231. }else{
  232. // 可捕获的服务器错误
  233. return [{message:result.msg},null];
  234. }
  235. },
  236. // 加载展示块默认数据
  237. async getNewsSearch(searchParam){
  238. console.log(searchParam)
  239. if(this.newsSearch.type.val !== this.newsSearch.type.oldVal){
  240. searchParam.p = 1;
  241. }
  242. searchParam.type = this.newsSearch.type.val;
  243. searchParam.l = this.limit;
  244. searchParam.p = searchParam.page;
  245. let [err,res] = await handle(
  246. this.$axios.get(
  247. apiMap.searchNewsMini.path,
  248. {params:searchParam})
  249. );
  250. if(err){
  251. console.log(err);
  252. return [{message:'请求数据失败'},null];
  253. }
  254. let result = res.data;
  255. if(result.code === rCode.OK){
  256. this.newsSearch.type.oldVal = this.newsSearch.type.val;
  257. // data 转换
  258. result.data = result.data.map(item=>{
  259. item.showText = item.name;
  260. return item;
  261. });
  262. return [null,result];
  263. }else{
  264. // 可捕获的服务器错误
  265. return [{message:result.msg},null];
  266. }
  267. },
  268. checkFormItem(field,enumOptions,reCheckField){
  269. let formItem = this.form[field];
  270. if (formItem){
  271. if (enumOptions){
  272. // 遍历枚举
  273. for (let i = 0; i < enumOptions.length; i++) {
  274. let enumOption = enumOptions[i];
  275. if (enumOption.value === formItem.val){
  276. return true;
  277. }
  278. }
  279. formItem.msg = '选项不在范围内';
  280. return false;
  281. }
  282. if(reCheckField){
  283. // 检查用字段
  284. formItem.msg = fieldIsAllow({
  285. [reCheckField]:formItem.val,
  286. })
  287. }else{
  288. formItem.msg = fieldIsAllow({
  289. [field]:formItem.val,
  290. })
  291. }
  292. }else{
  293. let r = true;
  294. for (const fieldKey in this.form) {
  295. formItem = this.form[fieldKey];
  296. let depend = this.form[formItem.depend];
  297. let checkField = fieldKey;
  298. if(formItem.reCheckField){
  299. checkField = formItem.reCheckField;
  300. }
  301. // 枚举值判断
  302. if(formItem.options){
  303. // 有枚举字段,只判断是否在枚举中
  304. if(formItem.options.findIndex(item=>item.value == formItem.val) === -1){
  305. formItem.msg = '选项不在范围内';
  306. r = false;
  307. }
  308. // 枚举值判断完毕,继续下一个字段
  309. continue;
  310. }
  311. // 判断是否有依赖字段
  312. if(depend){
  313. if(depend.options){
  314. // 依赖的对象有枚举类型,检查该枚举类型是否有有检测值
  315. let optionItem = depend.options.find(item=>item.value == depend.val);
  316. if(!optionItem){
  317. depend.msg = '选项不在范围内';
  318. formItem.msg = '该值依赖项输入异常';
  319. r = false;
  320. continue;
  321. }
  322. if(optionItem.checkField){
  323. console.log(`采用依赖项的检测字段${optionItem.checkField}`)
  324. checkField = optionItem.checkField;
  325. }
  326. }
  327. }
  328. console.log(`检测字段:${checkField},值:${formItem.val}`);
  329. formItem.msg = fieldIsAllow({
  330. [checkField]:formItem.val,
  331. })
  332. if (formItem.msg){
  333. r = false;
  334. }
  335. }
  336. return r
  337. }
  338. },
  339. initShowBlockForm(){
  340. this.showBlockData = {};
  341. let keys = Object.keys(this.form);
  342. for(let i = 0; i < keys.length; i++){
  343. let key = keys[i];
  344. this.form[key].val = this.form[key].init;
  345. this.form[key].msg = '';
  346. this.form[key].state = 0;
  347. this.form.value.showText = '';
  348. }
  349. },
  350. openAddShowBlockModal(){
  351. // 初始化表单
  352. this.initShowBlockForm();
  353. this.showBlockPopShow = true;
  354. this.isEditShowBlock = false;
  355. this.$nextTick(()=>{
  356. // 打开弹窗. 选择图片,填写链接地址,排序
  357. this.productSearch.type.options = this.productTypes;
  358. this.newsSearch.type.options = this.newsTypes;
  359. // 默认值设置
  360. this.productSearch.type.val = this.productTypes[0].key;
  361. this.productSearch.type.oldVal = this.productTypes[0].key;
  362. this.productSearch.type.init = this.productTypes[0].key;
  363. this.newsSearch.type.val = this.newsTypes[0].key;
  364. this.newsSearch.type.oldVal = this.newsTypes[0].key;
  365. this.newsSearch.type.init = this.newsTypes[0].key;
  366. });
  367. },
  368. onPopOkClickHandle(){
  369. if(this.isEditShowBlock){
  370. console.log('保存修改后的展示块数据');
  371. this.updateShowBlockExecute();
  372. }else{
  373. console.log('新增展示块');
  374. this.addShowBlockExecute();
  375. }
  376. },
  377. async addShowBlockExecute(){
  378. // 生成新数据表单
  379. let showBlockData = {};
  380. let isPass = this.checkFormItem();
  381. if(!isPass){
  382. return console.log('数据验证不通过');
  383. }
  384. console.log('开始生成新数据表单');
  385. showBlockData.sort = this.form.sort.val;
  386. showBlockData.type = this.form.type.val;
  387. showBlockData.value = this.form.value.val;
  388. showBlockData.title = this.form.title.val;
  389. showBlockData.subTitle = this.form.subTitle.val;
  390. showBlockData.fileId = this.form.fileData.val;
  391. showBlockData.state = this.form.state.val;
  392. // 生成新数据表单完毕
  393. console.log('生成新数据表单完毕');
  394. this.showBlockPopLoading = true;
  395. let [err,res] = await handle(this.$axios.put(apiMap.baseAddShowBlock.path,showBlockData));
  396. this.showBlockPopLoading = false;
  397. if(err){
  398. console.log(err);
  399. return this.$message.error('新增展示块失败');
  400. }
  401. this.$message.success('新增展示块成功');
  402. this.showBlockPopShow = false;
  403. await this.getShowBlockList();
  404. },
  405. async updateShowBlockExecute(){
  406. let showBlockData = {};
  407. let isPass = this.checkFormItem();
  408. if(!isPass){
  409. return console.log('数据验证不通过');
  410. }
  411. // 获取更新项
  412. console.log(`获取更新项`);
  413. let updateItems = {};
  414. console.log(this.showBlockData);
  415. let blockId = this.showBlockData.id;
  416. if(this.form.sort.val != toNumber(this.showBlockData.sort)){
  417. updateItems.sort = this.form.sort.val;
  418. }
  419. if(this.form.type.val != toNumber(this.showBlockData.type)){
  420. updateItems.type = this.form.type.val;
  421. }
  422. if(this.form.value.val != this.showBlockData.value){
  423. updateItems.value = this.form.value.val;
  424. }
  425. if(this.form.title.val != this.showBlockData.title){
  426. updateItems.title = this.form.title.val;
  427. }
  428. if(this.form.subTitle.val != this.showBlockData.subTitle){
  429. updateItems.subTitle = this.form.subTitle.val;
  430. }
  431. if(toNumber(this.form.fileData.val) != this.showBlockData.fileId){
  432. updateItems.fileId = this.form.fileData.val;
  433. }
  434. if(this.form.state.val != this.showBlockData.state){
  435. updateItems.state = this.form.state.val;
  436. }
  437. if(isEmpty(updateItems)){
  438. return this.$message.warn('未修改任何数据');
  439. }
  440. console.log(`更新展示块数据,更新数量: ${Object.keys(updateItems).length} 更新字段: [${Object.keys(updateItems).join(',')}]`);
  441. this.showBlockPopLoading = true;
  442. let [err,res] = await handle(this.$axios.post(apiMap.baseUpdateShowBlock.path,{
  443. blockId,
  444. updateItems
  445. }));
  446. this.showBlockPopLoading = false;
  447. if(err){
  448. console.log(err);
  449. return this.$message.error('更新展示块失败');
  450. }
  451. let result = res.data;
  452. if (result.code === rCode.OK){
  453. this.$message.success('更新展示块成功');
  454. this.showBlockPopShow = false;
  455. await this.getShowBlockList();
  456. }else{
  457. this.$message.error(`更新展示块失败,${result.msg}`);
  458. }
  459. },
  460. getShowBlockTypeText(type){
  461. type = toNumber(type);
  462. let typeText = '';
  463. if(type === dbField_esm.db_base.carouselType.production){
  464. typeText = '产品';
  465. }else if(type === dbField_esm.db_base.carouselType.news){
  466. typeText = '文章';
  467. }else if(type === dbField_esm.db_base.carouselType.href){
  468. typeText = '直接链接';
  469. }else{
  470. typeText = '暂未支持类型';
  471. }
  472. return typeText
  473. },
  474. getShowBlockStateText(state){
  475. // state = toNumber(state);
  476. let typeText = '';
  477. if(state === dbField_esm.db_base.carouselState.enable){
  478. typeText = '启用';
  479. }else if(state === dbField_esm.db_base.carouselState.disable){
  480. typeText = '禁用';
  481. }else{
  482. typeText = '未知状态';
  483. }
  484. return typeText
  485. },
  486. cancelPop(){
  487. this.imageSelectVisible = false;
  488. },
  489. okHandle(fileItem){
  490. console.log('文件列表');
  491. console.log(fileItem);
  492. this.cancelPop();
  493. this.$nextTick(()=>{
  494. this.form.fileData.val = fileItem.fileId;
  495. this.form.fileData.state = 1;
  496. this.form.fileData.msg = '';
  497. this.form.fileData.showText = fileItem.filePath;
  498. })
  499. },
  500. onProductSearchHandle(e){
  501. console.log(`onProductSearchHandle ${e}`);
  502. console.log(e);
  503. console.log(this.productSearch.type.val);
  504. },
  505. onSelectedItemHandle(item){
  506. console.log(`selected item ${item}`);
  507. console.log(item);
  508. this.form.value.val = item.id;
  509. this.form.value.showText = item.showText;
  510. this.form.value.msg = '';
  511. },
  512. onTypeChangeHandle(e){
  513. console.log(`type change ${e}`);
  514. // 清除其他值
  515. this.form.value.val = '';
  516. this.form.value.msg = '';
  517. this.form.value.showText = '';
  518. },
  519. onNewsSearchHandle(e){
  520. console.log(`onProductSearchHandle ${e}`);
  521. console.log(e);
  522. console.log(this.productSearch.type.val);
  523. },
  524. onClickEditHandle(item){
  525. console.log(`点击编辑展示块`);
  526. console.log(item);
  527. if(!item || !item.id){
  528. return this.$message.warn('展示块数据获取异常,已经取消编辑');
  529. }
  530. this.initShowBlockForm();
  531. this.isEditShowBlock = true;
  532. this.showBlockData = item;
  533. this.showBlockPopShow = true;
  534. this.showBlockPopLoading = true;
  535. this.form.sort.val = toNumber(item.sort);
  536. this.form.sort.init = toNumber(item.sort);
  537. this.form.type.val = toNumber(item.type);
  538. this.form.type.init = toNumber(item.type);
  539. this.form.value.val = item.value;
  540. this.form.value.init = item.value;
  541. this.form.value.showText = item.valueShowText;
  542. this.form.value.oldShowText = item.oldShowText;
  543. this.form.title.val = item.title;
  544. this.form.title.init = item.title;
  545. this.form.subTitle.val = item.subTitle;
  546. this.form.subTitle.init = item.subTitle;
  547. this.form.fileData.val = item.fileId;
  548. this.form.fileData.init = item.fileId;
  549. this.form.fileData.showText = item.filePath;
  550. this.form.fileData.oldShowText = item.filePath;
  551. this.form.state.val = item.state;
  552. this.showBlockPopLoading = false;
  553. }
  554. },
  555. })
  556. </script>
  557. <template>
  558. <div class="w-full p-2">
  559. <rounded-title>展示块管理</rounded-title>
  560. <div class="mt-2 rounded bg-white p-2">
  561. <div class="mt-2 rounded bg-white p-2">
  562. <div class="py-1 border-b border-cyan-300 flex justify-between">
  563. 点击下方块进行管理展示块数据
  564. <div class="px-2 flex"><!-- 刷新按钮-->
  565. <a-button type="primary" class="ant-icon-btn " icon="reload" @click="getShowBlockList" :loading="loading"></a-button>
  566. </div>
  567. </div>
  568. <div class="w-full h-auto transition">
  569. <div v-show="loading" class="w-full h-64 flex justify-center items-center ">
  570. <a-spin size="large" />
  571. </div>
  572. <!-- 展示块预览区域,保持与 showingStand.vue 一致的布局 -->
  573. <div class="container mx-auto showing">
  574. <div
  575. v-for="(item,i) in showBlockList"
  576. :class="`chunk imgBox chunk${(i+1)}`"
  577. :key="item.id"
  578. >
  579. <!-- 实时管理(物联网) -->
  580. <img :src="item.filePath" alt="item.title">
  581. <p class="chunkText">
  582. <span class="title">{{item.title}}</span>
  583. <span class="subTitle">{{item.subTitle}}</span>
  584. </p>
  585. <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity bg-black bg-opacity-30">
  586. <a-button @click="onClickEditHandle(item)" type="primary">编辑</a-button>
  587. </div>
  588. </div>
  589. </div>
  590. </div>
  591. </div>
  592. </div>
  593. <pop :show="showBlockPopShow" :loading="showBlockPopLoading">
  594. <pop-card>
  595. <!-- <template slot="header" class="w-full">-->
  596. <!-- 编辑展示块-->
  597. <!-- </template>-->
  598. <template slot="close-group">
  599. <a-button icon="close" @click="showBlockPopShow = false"></a-button>
  600. </template>
  601. <div class="w-full">
  602. <input-row :msg="form.sort.msg"
  603. label="排序">
  604. <a-input-number v-model="form.sort.val"
  605. @focus="form.sort.msg=''"
  606. :min="0"
  607. @blur="checkFormItem('sort')"
  608. />
  609. </input-row>
  610. <input-row :msg="form.state.msg"
  611. label="状态">
  612. <a-radio-group v-model="form.state.val">
  613. <a-radio-button v-for="opt in form.state.options"
  614. :key="'cState'+opt.value"
  615. :value="opt.value">
  616. {{ opt.label }}
  617. </a-radio-button>
  618. </a-radio-group>
  619. </input-row>
  620. <input-row :msg="form.title.msg"
  621. label="标题">
  622. <a-input v-model="form.title.val"
  623. placeholder="请输入标题"
  624. @focus="form.title.msg=''"
  625. @blur="checkFormItem('title')"
  626. />
  627. </input-row>
  628. <input-row :msg="form.subTitle.msg"
  629. label="副标题">
  630. <a-textarea v-model="form.subTitle.val"
  631. placeholder="请输入副标题"
  632. :rows="2"
  633. @focus="form.subTitle.msg=''"
  634. @blur="checkFormItem('subTitle')"
  635. />
  636. </input-row>
  637. <!-- 展示块类型选择 -->
  638. <input-row :msg="form.type.msg"
  639. label="展示块类型">
  640. <a-radio-group v-model="form.type.val" @change="onTypeChangeHandle">
  641. <a-radio-button v-for="opt in form.type.options"
  642. :key="'cType'+opt.value"
  643. :value="opt.value">
  644. {{ opt.label }}
  645. </a-radio-button>
  646. </a-radio-group>
  647. </input-row>
  648. <!-- 展示块具体值 -->
  649. <!-- 链接-->
  650. <input-row
  651. v-show="form.type.val === dbField_esm.db_base.carouselType.href"
  652. :msg="form.value.msg"
  653. label="输入链接">
  654. <a-input v-model="form.value.val"
  655. placeholder="输入要指向的链接地址"
  656. @focus="form.value.msg=''"
  657. @blur="checkFormItem('value', null,'href')"
  658. />
  659. </input-row>
  660. <!-- 产品选择-->
  661. <input-row
  662. v-show="form.type.val === dbField_esm.db_base.carouselType.production"
  663. :msg="form.value.msg"
  664. label="选择产品">
  665. {{ form.value.showText }}
  666. <a-popover v-model="productSelectVisible" title="选择产品" trigger="click">
  667. <div slot="content" class="searchBox" >
  668. <search-box
  669. class="h-72"
  670. :loadDataApi="getProductSearch"
  671. :limit="limit"
  672. search-placeholder="请输入产品名称关键字"
  673. loadTip="正在搜索产品中..."
  674. @onSelectedItem="onSelectedItemHandle"
  675. >
  676. <div class="w-full" slot="otherSearchItem">
  677. <a-radio-group v-model="productSearch.type.val"
  678. @change="onProductSearchHandle">
  679. <a-radio-button v-for="opt in productSearch.type.options"
  680. :key="'cType'+opt.key"
  681. :value="opt.key">
  682. {{ opt.text }}
  683. </a-radio-button>
  684. </a-radio-group>
  685. </div>
  686. </search-box>
  687. </div>
  688. <a-button type="primary" >
  689. 选择产品
  690. </a-button>
  691. </a-popover>
  692. </input-row>
  693. <!-- 新闻选择-->
  694. <input-row
  695. v-show="form.type.val === dbField_esm.db_base.carouselType.news"
  696. :msg="form.value.msg"
  697. label="文章选择">
  698. {{ form.value.showText }}
  699. <a-popover v-model="newsSelectVisible" title="选择你需要的文章" trigger="click">
  700. <div slot="content" class="searchBox" >
  701. <search-box
  702. class="h-72"
  703. :loadDataApi="getNewsSearch"
  704. :limit="limit"
  705. search-placeholder="请输入文章名称关键字"
  706. loadTip="正在搜索文章中..."
  707. ref="productSearch"
  708. @onSelectedItem="onSelectedItemHandle"
  709. >
  710. <div class="w-full" slot="otherSearchItem">
  711. <a-radio-group v-model="newsSearch.type.val"
  712. @change="onNewsSearchHandle">
  713. <a-radio-button v-for="opt in newsSearch.type.options"
  714. :key="'cType'+opt.key"
  715. :value="opt.key">
  716. {{ opt.text }}
  717. </a-radio-button>
  718. </a-radio-group>
  719. </div>
  720. </search-box>
  721. </div>
  722. <a-button type="primary">
  723. 选择文章
  724. </a-button>
  725. </a-popover>
  726. </input-row>
  727. <!-- 选择图片 -->
  728. <input-row label="展示图片"
  729. :msg="form.fileData.msg">
  730. <div class="w-full h-60 rounded relative" @click="imageSelectVisible = true">
  731. <image-viewer class="" :src="form.fileData.showText"></image-viewer>
  732. <div class="absolute w-full h-full left-0 top-0
  733. justify-center text-white bg-gray-400
  734. items-center text-2xl flex opacity-0 hover:opacity-70">
  735. 点击选择图片
  736. </div>
  737. </div>
  738. </input-row>
  739. </div>
  740. <template class="w-full" slot="footer">
  741. <a-button @click="onPopOkClickHandle">{{isEditShowBlock? '保存': '新增'}}</a-button>
  742. </template>
  743. </pop-card>
  744. </pop>
  745. <pop :show="imageSelectVisible">
  746. <image-table class="w-5/12 h-1/2"
  747. @cancel="imageSelectVisible = false"
  748. @ok="okHandle"></image-table>
  749. </pop>
  750. </div>
  751. </template>
  752. <style scoped>
  753. .searchBox{
  754. width: 420px;
  755. height: 520px;
  756. }
  757. .showing{
  758. display: flex;
  759. flex-direction: column;
  760. align-items: center;
  761. }
  762. .showing .chunk {
  763. box-shadow: 1px 1px 3px black;
  764. /*box-sizing: border-box;*/
  765. /*padding:5px;*/
  766. position: relative;
  767. overflow: hidden;
  768. cursor: pointer;
  769. width: 96%;
  770. aspect-ratio:500/350;
  771. margin-bottom: 10px;
  772. }
  773. .showing .chunk:hover > img{
  774. left: -20px;
  775. top: -20px;
  776. max-width: calc(100% + 40px);
  777. width: calc(100% + 40px);
  778. height: calc(100% + 40px);
  779. }
  780. .showing .chunk .chunkText{
  781. display: block;
  782. position: absolute;
  783. width: 100%;
  784. height: 150px;
  785. bottom: -50px;
  786. /*background: #000;*/
  787. color: white;
  788. transition: all .5s;
  789. padding: 0 15px;
  790. background-image: linear-gradient(rgba(0,0,0,0) 0,rgba(0,0,0,0.5) 75% );
  791. }
  792. .showing .chunk:hover .chunkText{
  793. bottom: 0;
  794. background-image: linear-gradient(rgba(0,0,0,0) 0,rgba(0,0,0,0.66) 66% 15% 80% );
  795. }
  796. .showing .chunk .chunkText .title{
  797. height: 40px;
  798. font-size: 1.5em;
  799. display: flex;
  800. /*justify-content: center;*/
  801. align-items: center;
  802. }
  803. .showing .chunk .chunkText .subTitle{
  804. padding: 5px 0;
  805. min-height: 35px;
  806. max-height: 80px;
  807. font-size: 1.2em;
  808. }
  809. .showing .chunk .chunkText .chunkMore{
  810. margin-top: 20px;
  811. height: 30px;
  812. font-size: 0.9em;
  813. color: #ff6e3f;
  814. }
  815. .showing .chunk .chunkText .chunkMore:hover{
  816. color: orangered;
  817. }
  818. .showing .chunk .chunkText > *{
  819. display: block;
  820. }
  821. .showing .chunk1 {
  822. grid-area: a;
  823. }
  824. .showing .chunk2{
  825. grid-area: b;
  826. }
  827. .showing .chunk3{
  828. grid-area: c;
  829. aspect-ratio:500/810;
  830. }
  831. .showing .chunk4{
  832. grid-area: d;
  833. aspect-ratio:870/400;
  834. }
  835. .showing .chunk5{
  836. grid-area: e;
  837. }
  838. .showing .chunk6{
  839. grid-area: f;
  840. }
  841. .showing .chunk7{
  842. grid-area: g;
  843. }
  844. @media screen and (min-width: 1024px) {
  845. /* 其他针对移动设备的样式 */
  846. .showing{
  847. display: grid;
  848. grid-template-columns: repeat(3, 500px);
  849. grid-template-rows: repeat(3, 410px);
  850. grid-template-areas:
  851. 'a b c'
  852. 'd d c'
  853. 'e f g';
  854. grid-gap: 20px;
  855. }
  856. .showing .chunk {
  857. box-shadow: 1px 1px 3px black;
  858. box-sizing: border-box;
  859. /*padding:5px;*/
  860. position: relative;
  861. overflow: hidden;
  862. cursor: pointer;
  863. aspect-ratio:500/410;
  864. margin-bottom: 0;
  865. width: 100%;
  866. }
  867. .showing .chunk:hover{
  868. box-shadow: 1px 1px 3px deepskyblue;
  869. }
  870. .showing .chunk3{
  871. grid-area: c;
  872. aspect-ratio:500/840;
  873. }
  874. .showing .chunk4{
  875. grid-area: d;
  876. aspect-ratio:870/350;
  877. }
  878. }
  879. .showing .chunk .chunkText .title{
  880. height: 40px;
  881. font-size: 1.5em;
  882. display: flex;
  883. align-items: center;
  884. }
  885. .showing .chunk .chunkText .subTitle{
  886. padding: 5px 0;
  887. min-height: 35px;
  888. max-height: 80px;
  889. font-size: 1.2em;
  890. }
  891. .showing .chunk1 {
  892. grid-area: a;
  893. }
  894. .showing .chunk2{
  895. grid-area: b;
  896. }
  897. .showing .chunk3{
  898. grid-area: c;
  899. aspect-ratio:500/810;
  900. }
  901. .showing .chunk4{
  902. grid-area: d;
  903. aspect-ratio:870/400;
  904. }
  905. .showing .chunk5{
  906. grid-area: e;
  907. }
  908. .showing .chunk6{
  909. grid-area: f;
  910. }
  911. .showing .chunk7{
  912. grid-area: g;
  913. }
  914. @media screen and (min-width: 1024px) {
  915. .showing{
  916. display: grid;
  917. grid-template-columns: repeat(3, 500px);
  918. grid-template-rows: repeat(3, 410px);
  919. grid-template-areas:
  920. 'a b c'
  921. 'd d c'
  922. 'e f g';
  923. grid-gap: 20px;
  924. }
  925. .showing .chunk {
  926. box-shadow: 1px 1px 3px black;
  927. position: relative;
  928. overflow: hidden;
  929. cursor: pointer;
  930. aspect-ratio:500/410;
  931. margin-bottom: 0;
  932. width: 100%;
  933. }
  934. .showing .chunk:hover{
  935. box-shadow: 1px 1px 3px deepskyblue;
  936. }
  937. .showing .chunk3{
  938. grid-area: c;
  939. aspect-ratio:500/840;
  940. }
  941. .showing .chunk4{
  942. grid-area: d;
  943. aspect-ratio:870/350;
  944. }
  945. }
  946. </style>