| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- <script >
- import RoundedTitle from "@/components/public/roundedTitle.vue";
- import TableSelect from "@/components/public/tableSelect.vue";
- import InputRow from "@/components/public/form/inputRow.vue";
- import ImageViewer from "@/components/public/imageViewer.vue";
- import Pop from "@/components/public/pop.vue";
- import PopCard from "@/components/public/popCard.vue";
- import ImageTable from "@/components/public/imageTable.vue";
- import {escapeHtml, unescapeHtml} from "@/until/unescapeHtml";
- import langMap from "@/map/langMap";
- import {initForm} from "@/until/formTool";
- import {FormVerify} from "kind-form-verify";
- import {fieldCheck} from "@/until/form/fieldVerify";
- import {isEmpty} from "@/until/typeTool";
- import {apiMap} from "@/map/apiMap";
- import {handle} from "@/until/handle";
- import {rCode} from "@/map/rcodeMap_esm";
- let ClassicEditor;
- if (process.client) {
- ClassicEditor = require('../../../model/ckeditor/ckeditor');
- }
- let formVerify = null;
- let baseEditConfig = {
- language: 'zh',
- customText: "插入图片",
- }
- const imageSelectType = {
- main: "main",
- sub: "sub",
- editor: "editor",
- }
- export default {
- name: "productAdd",
- components: {ImageTable, PopCard, Pop, ImageViewer, InputRow, TableSelect, RoundedTitle},
- props: ['product'],
- data() {
- return {
- isEdit: false,
- editId: null,
- overviewEditorConfig: { ...baseEditConfig },
- detailEditorConfig: { ...baseEditConfig },
- parameterEditorConfig: { ...baseEditConfig },
- overviewEditor: ClassicEditor,
- detailEditor: ClassicEditor,
- parameterEditor: ClassicEditor,
- overviewEditorData: '<p>这是产品的简介</p>',
- detailEditorData: '<p>这是产品的详细介绍</p>',
- parameterEditorData: '<p>这是产品的参数介绍, 推荐使用表格详细介绍</p>',
- langMap: langMap.lang,
- form: {
- type: {
- val: 'all',
- init: '',
- msg: '',
- state: 0,
- options: this.$store.getters.productTypes,
- disables: ['all'],
- },
- name: {
- val: '',
- init: '',
- msg: '',
- state: 0
- },
- remark: {
- val: '',
- init: '',
- msg: '',
- state: 0
- },
- sort: {
- val: 0,
- init: 0,
- msg: '',
- state: 0
- },
- image: {
- val: '',
- init: '',
- msg: '',
- reCheckField: 'fileData',
- showText: '',
- state: 0
- },
- seo_key: {
- val: "",
- oldVal: "",
- init: "",
- msg: "",
- state: 0,
- }
- },
- sub_images: [],
- imageSelectType: imageSelectType,
- // 图片选择的参数
- imagePopShow: false,
- imagePopLoading: false,
- imageSelect: {
- type: '',
- param: ''
- },
- previewShow: false,
- previewLoading: false,
- previewData: {
- },
- }
- },
- watch: {
- product(val){
- // 更新文章数据
- this.editModel()
- }
- },
- computed: {
- tags () {
- // 拆分tags
- let tags = this.form.seo_key.val
- if (!tags) {
- return [];
- }
- // 去除空字符串
- tags = tags.replace(/\s/g,'')
- tags = tags.replace(/,|;/g,',')
- let tagArr = tags.split(',')
- // 标签去重
- tagArr = [...new Set(tagArr)]
- // 移除空标签
- tagArr = tagArr.filter(item => item)
- return tagArr
- }
- },
- beforeMount() {
- this.detailEditorConfig.customFunction = () => {
- this.openImageSelect(imageSelectType.editor, 'detail');
- };
- this.overviewEditorConfig.customFunction = () => {
- this.openImageSelect(imageSelectType.editor, 'overview');
- };
- this.parameterEditorConfig.customFunction = () => {
- this.openImageSelect(imageSelectType.editor, 'parameter');
- };
- },
- mounted() {
- formVerify = new FormVerify(
- this.form,
- fieldCheck,
- )
- // fieldCheck.checkField('type',this.form.type.val)
- // formVerify.checkForm(this.form, true);
- console.log(formVerify);
- formVerify.onLog = (msg) => {
- console.log(msg);
- };
- // 判断是否为编辑文章
- if(!isEmpty(this.product)){
- this.isEdit = true;
- }
- this.initForm();
- },
- beforeDestroy() {
- formVerify = null;
- },
- methods: {
- initForm(){
- initForm(this.form);
- this.form.image.showText = this.form.image.val;
- // 强制更新页面
- this.$forceUpdate()
- },
- /**
- * 图片路径处理, 兼容手动上传的图片与后期自动增加的图片
- * @param text
- * @return {*|string|string}
- */
- imagePathBabel(text){
- return text?text.charAt(0) == '/'? text : '/public/'+text : ''
- },
- openImageSelect(type, param){
- console.log('打开文章封面选择窗口');
- this.imageSelect = {type, param}
- this.imagePopShow = true;
- },
- deleteTag(tag){
- let tagArr = this.tags
- let index = tagArr.indexOf(tag)
- tagArr.splice(index, 1)
- this.form.seo_key.val = tagArr.join(',')
- this.form.seo_key.msg = ''
- },
- // 子图转换
- translateSubImage(arr){
- let sub_images = arr.map((item)=>{
- return {
- id: item.id,
- isAdd: false,
- path: item.path
- }
- })
- return sub_images
- },
- addSubImage(){
- this.sub_images.push('')
- },
- deleteSubImage(i){
- this.sub_images.splice(i, 1)
- },
- editorAddImage(editor, fileData){
- switch (editor) {
- case 'overview':
- this.overviewEditorData += `<img src="${fileData.filePath}" alt="${fileData.fileName}">`;
- break;
- case 'detail':
- this.detailEditorData += `<img src="${fileData.filePath}" alt="${fileData.fileName}">`;
- break;
- case 'parameter':
- this.parameterEditorData += `<img src="${fileData.filePath}" alt="${fileData.fileName}">`;
- break;
- }
- },
- selectImageHandle(fileData){
- console.log('选择图片')
- console.log(fileData)
- this.imagePopShow = false;
- switch (this.imageSelect.type) {
- case imageSelectType.main:
- console.log('选择产品主图');
- this.form.image.val = fileData.filePath
- this.form.image.msg = '';
- this.form.image.state = 0;
- this.form.image.showText = fileData.filePath
- console.log(fileData.path)
- console.log(this.form.image)
- break;
- case imageSelectType.sub:
- console.log(`选择产品子图 param:${this.imageSelect.param}`);
- this.sub_images[this.imageSelect.param] = fileData.filePath
- break;
- case imageSelectType.editor:
- this.editorAddImage(this.imageSelect.param, fileData)
- break;
- default:
- console.log('未知类型选择图片');
- break;
- }
- },
- editModel(){
- console.log(`editModel`)
- this.isEdit = true;
- this.editId = this.product.proid;
- this.form.type.init = this.product.type_key;
- this.form.name.init = this.product.name;
- this.form.sort.init = this.product.sort;
- this.form.remark.init = this.product.remark;
- this.form.image.init = this.product.image;
- this.form.seo_key.init = this.product.seo_key;
- this.sub_images = this.product.sub_images;
- this.overviewEditorData = unescapeHtml(this.product.overview) || "";
- this.detailEditorData = unescapeHtml(this.product.detail) || "";
- this.parameterEditorData = unescapeHtml(this.product.parameter) || "";
- this.initForm()
- },
- async onSubmitHandle(){
- console.log('提交产品');
- let isPass = formVerify.checkForm(this.form, true);
- if(!isPass){
- console.log(this.form);
- this.$message.error('数据验证不通过');
- return console.log('数据验证不通过');
- }
- let formData = formVerify.getFormData();
- formData.detail = escapeHtml(this.detailEditorData);
- formData.overview = escapeHtml(this.overviewEditorData);
- formData.parameter = escapeHtml(this.parameterEditorData);
- formData.sub_images = this.sub_images;
- let queryPath = this.isEdit? apiMap.productEdit : apiMap.productAdd;
- let actionText = this.isEdit? '修改产品': '新增产品';
- if(this.isEdit){
- formData.proid = this.editId;
- }
- let [err,res] = await handle(this.$axios.post(
- queryPath.path,
- formData
- ));
- if(err){
- console.log(err);
- return this.$message.error(`${actionText}失败`);
- }
- let result = res.data;
- if (result.code === rCode.OK){
- this.$message.success(`${actionText}成功`);
- }else{
- this.$message.error(`${actionText}失败,${result.msg}`);
- }
- },
- preview(){
- console.log('预览产品');
- this.previewShow = true;
- this.previewLoading = true;
- setTimeout(()=>{
- this.$nextTick(()=>{
- this.previewLoading = false;
- this.previewData = this.getPreviewData();
- // 强制刷新
- this.$forceUpdate()
- })
- }, 500)
- },
- cancelPreview(){
- console.log('关闭预览')
- this.previewLoading = false;
- this.previewShow = false;
- },
- getPreviewData(){
- let data = {
- proid: this.editId,
- type_key: this.form.type.val,
- name: this.form.name.val,
- sort: this.form.sort.val,
- remark: this.form.remark.val,
- image: this.form.image.val,
- seo_key: this.form.seo_key.val,
- overview: this.overviewEditorData,
- detail: this.detailEditorData,
- parameter: this.parameterEditorData,
- sub_images: this.sub_images
- }
- return data
- }
- }
- }
- </script>
- <template>
- <div class="w-full p-2">
- <rounded-title class="text-xl flex ">
- <span>{{ isEdit? "编辑产品" : "新增产品" }}</span>
- <a href="/manger/product"
- class="edit-btn custom-btn btn-13 ml-2">
- 产品列表
- </a>
- </rounded-title>
- <div class="page-content">
- <input-row class="mt-3"
- :msg="form.type.msg"
- :label-width="120"
- label="产品分类">
- <table-select
- class="w-48 flex-shrink-0"
- :options="form.type.options"
- v-model="form.type.val"
- />
- </input-row>
- <input-row class="mt-3"
- :msg="form.name.msg"
- :label-width="120"
- label="产品名称">
- <a-input class="!w-48 "
- v-model="form.name.val"
- placeholder="请输入产品名称"></a-input>
- </input-row>
- <input-row class="mt-3"
- :msg="form.remark.msg"
- :label-width="120"
- label="产品简述">
- <a-input
- v-model="form.remark.val"
- placeholder="请输入产品简述"></a-input>
- </input-row>
- <input-row class="mt-3"
- :msg="form.sort.msg"
- :label-width="120"
- label="产品排序优先级">
- <a-input-number class="!w-48 "
- v-model="form.sort.val"
- :min="0"
- :max="9999"
- ></a-input-number>
- </input-row>
- <input-row class="mt-3"
- :msg="form.seo_key.msg"
- :label-width="120"
- label="seo关键字"
- remark="用于增加产品再搜索引擎中的关键字, 要确保准确有效"
- >
- <a-tooltip>
- <template slot="title">
- 搜索引擎关注的关键字,多个用英文逗号隔开
- </template>
- <a-input v-model="form.seo_key.val" placeholder="seo关键字"></a-input>
- </a-tooltip>
- <div class="tags">
- <a-tag
- v-for="tag in tags"
- :key="tag"
- color="#2db7f5"
- closable @close="deleteTag"
- >
- {{ tag }}
- </a-tag>
- </div>
- </input-row>
- <input-row class="mt-3"
- :msg="form.image.msg"
- :label-width="120"
- label="产品主图"
- remark="也会作为产品会显示的的封面"
- >
- <div class="rounded relative" style="width: 524px;height: 360px" >
- <image-viewer class="" :src="imagePathBabel(form.image.showText)"
- :alt="form.image.showText" ></image-viewer>
- <div class="absolute w-full h-full left-0 top-0
- justify-center text-white bg-gray-400
- items-center text-2xl flex opacity-0 hover:opacity-70"
- @click="openImageSelect(imageSelectType.main)"
- >
- 点击选择产品图片
- </div>
- </div>
- </input-row>
- <input-row class="mt-3"
- :label-width="120"
- label="产品子图"
- remark="产品更多的缩略图, 可以为空"
- >
- <div class="sub-image">
- <!-- 新增按钮 -->
- <div class="image-add">
- <a-button type="primary" @click="addSubImage">
- <a-icon type="plus" />
- 添加子图
- </a-button>
- </div>
- <div class="image-list">
- <div class="image-item"
- v-for="(item, i) in sub_images"
- :key="`sub_img_${item}`"
- >
- <div class="image-item-content">
- <image-viewer
- :src="imagePathBabel(item)"
- ></image-viewer>
- <div class="absolute w-full h-full left-0 top-0
- justify-center text-white bg-gray-400
- items-center text-xl flex opacity-0 hover:opacity-70"
- @click="openImageSelect(imageSelectType.sub, i)"
- >
- 选择图片
- </div>
- </div>
- <div class="option">
- <a-button type="danger"
- @click="deleteSubImage(i)">
- 移除
- </a-button>
- </div>
- </div>
- </div>
- </div>
- </input-row>
- <input-row class="mt-3"
- :label-width="120"
- label="产品简介"
- remark="产品的简要介绍"
- >
- <ckeditor class="mt-2" ref="ckeditor"
- :editor="overviewEditor"
- v-model="overviewEditorData"
- :config="overviewEditorConfig"></ckeditor>
- </input-row>
- <input-row class="mt-3"
- :label-width="120"
- label="产品详情"
- remark="产品的详细介绍"
- >
- <ckeditor class="mt-2" ref="ckeditor"
- :editor="detailEditor"
- v-model="detailEditorData"
- :config="detailEditorConfig"></ckeditor>
- </input-row>
- <input-row class="mt-3"
- :label-width="120"
- label="产品参数"
- remark="产品的参数配置信息, 推荐使用表格"
- >
- <ckeditor class="mt-2" ref="ckeditor"
- :editor="parameterEditor"
- v-model="parameterEditorData"
- :config="parameterEditorConfig"></ckeditor>
- </input-row>
- <div class="mt-2">
- <a-button type="primary" @click="onSubmitHandle">
- {{isEdit?"保存修改":"新增产品"}}
- </a-button>
- <a-button class="ml-2" @click="preview">
- 预览
- </a-button>
- </div>
- </div>
- <!-- 图片选择弹窗 -->
- <pop :show="imagePopShow" :loading="imagePopLoading">
- <pop-card>
- <template slot="header">
- <span>选择需要插入的图片</span>
- </template>
- <image-table
- class="w-full h-full"
- @cancel="imagePopShow = false"
- @ok="selectImageHandle">
- </image-table>
- </pop-card>
- </pop>
- <pop :show="previewShow" :loading="previewLoading"
- >
- <div class="product-show">
- <!-- 关闭按钮 -->
- <product-info
- :product="previewData"
- :unescape="false"
- lang="lang"
- :productId="editId"
- />
- <div class="absolute top-0 right-0 p-2 text-red-500 text-xl">
- <a-icon type="close" @click="cancelPreview"/>
- </div>
- </div>
- </pop>
- </div>
- </template>
- <style scoped>
- .page-content{
- margin-top: 20px;
- width: 100%;
- height: calc(100% - 50px);
- overflow: auto;
- background-color: #fff;
- border-radius: 10px;
- box-sizing: border-box;
- padding: 10px;
- }
- .edit-btn{
- font-size: 14px;
- }
- .tags{
- width: 100%;
- display: flex;
- flex-wrap: wrap;
- justify-content: flex-start;
- align-items: center;
- padding: 5px;
- box-sizing: border-box;
- }
- .sub-image{
- width: 100%;
- max-width: 520px;
- height: auto;
- padding: 0.5rem;
- border-radius: 10px;
- box-sizing: border-box;
- background-color: #f5f5f5;
- border: 1px solid #e5e5e5;
- }
- .image-add{
- width: 100%;
- height: 40px;
- border-bottom: 1px solid #e5e5e5;
- }
- .image-list{
- width: 100%;
- height: auto;
- max-height: 550px;
- overflow: auto;
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
- .image-item{
- width: calc(100% - 10px);
- margin: 5px 0;
- height: 100px;
- display: flex;
- justify-content: space-between;
- }
- .image-item-content{
- width: 100px;
- height: 100px;
- position: relative;
- }
- .option{
- width: 100px;
- height: 100px;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .product-show{
- width: 80%;
- height: calc(100% - 50px);
- overflow: auto;
- background-color: #fff;
- border-radius: 10px;
- position: relative;
- }
- </style>
|