Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/notify
# Conflicts: # sql/mysql/ruoyi-vue-pro.sql # yudao-ui-admin/src/utils/dict.js
This commit is contained in:
@@ -6,19 +6,19 @@
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="定义编号" align="center" prop="id" width="400" />
|
||||
<el-table-column label="定义名称" align="center" prop="name" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" @click="handleBpmnDetail(scope.row)">
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="定义分类" align="center" prop="category" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="表单信息" align="center" prop="formType" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button v-if="scope.row.formId" type="text" @click="handleFormDetail(scope.row)">
|
||||
<span>{{ scope.row.formName }}</span>
|
||||
</el-button>
|
||||
@@ -29,25 +29,25 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-tag size="medium" v-if="scope.row">v{{ scope.row.version }}</el-tag>
|
||||
<el-tag size="medium" type="warning" v-else>未部署</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="version" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
|
||||
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.deploymentTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="定义描述" align="center" prop="description" width="300" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
|
||||
v-hasPermi="['bpm:task-assign-rule:update']">分配规则</el-button>
|
||||
</template>
|
||||
@@ -76,7 +76,6 @@
|
||||
<script>
|
||||
import {getProcessDefinitionBpmnXML, getProcessDefinitionPage} from "@/api/bpm/definition";
|
||||
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
|
||||
import {getForm} from "@/api/bpm/form";
|
||||
import {decodeFields} from "@/utils/formGenerator";
|
||||
import Parser from '@/components/parser/Parser'
|
||||
import taskAssignRuleDialog from "../taskAssignRule/taskAssignRuleDialog";
|
||||
|
||||
@@ -151,9 +151,7 @@ import RightPanel from '@/views/infra/build/RightPanel'
|
||||
import {
|
||||
inputComponents, selectComponents, layoutComponents, formConf
|
||||
} from '@/components/generator/config'
|
||||
import {
|
||||
exportDefault, beautifierConf, isNumberStr, titleCase, deepClone, isObjectObject
|
||||
} from '@/utils/index'
|
||||
import {beautifierConf, titleCase, deepClone, isObjectObject} from '@/utils'
|
||||
import {
|
||||
makeUpHtml, vueTemplate, vueScript, cssStyle
|
||||
} from '@/components/generator/html'
|
||||
|
||||
@@ -27,18 +27,18 @@
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="表单名" align="center" prop="name" />
|
||||
<el-table-column label="开启状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)"
|
||||
v-hasPermi="['bpm:form:query']">详情</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
|
||||
@@ -38,24 +38,24 @@
|
||||
<el-table-column label="组名" align="center" prop="name" />
|
||||
<el-table-column label="描述" align="center" prop="description" />
|
||||
<el-table-column label="成员" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span v-for="userId in scope.row.memberUserIds">
|
||||
{{ getUserNickname(userId) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['bpm:user-group:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
@@ -127,7 +127,6 @@ export default {
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
tenantId: null,
|
||||
createTime: []
|
||||
},
|
||||
// 表单参数
|
||||
|
||||
@@ -40,19 +40,19 @@
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="流程标识" align="center" prop="key" />
|
||||
<el-table-column label="流程名称" align="center" prop="name" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" @click="handleBpmnDetail(scope.row)">
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程分类" align="center" prop="category" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="表单信息" align="center" prop="formType" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button v-if="scope.row.formId" type="text" @click="handleFormDetail(scope.row)">
|
||||
<span>{{ scope.row.formName }}</span>
|
||||
</el-button>
|
||||
@@ -63,31 +63,31 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最新部署的流程定义" align="center">
|
||||
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-tag size="medium" v-if="scope.row.processDefinition">v{{ scope.row.processDefinition.version }}</el-tag>
|
||||
<el-tag size="medium" type="warning" v-else>未部署</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="激活状态" align="center" prop="processDefinition.version" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-switch v-if="scope.row.processDefinition" v-model="scope.row.processDefinition.suspensionState"
|
||||
:active-value="1" :inactive-value="2" @change="handleChangeState(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span v-if="scope.row.processDefinition">{{ parseTime(scope.row.processDefinition.deploymentTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="450" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['bpm:model:update']">修改流程</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-setting" @click="handleDesign(scope.row)"
|
||||
@@ -489,7 +489,10 @@ export default {
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess(statusState + "成功");
|
||||
}).catch(() => {});
|
||||
}).catch(() => {
|
||||
// 取消后,进行恢复按钮
|
||||
row.processDefinition.suspensionState = (state === 1 ? 2 : 1);
|
||||
});
|
||||
},
|
||||
/** 导入按钮操作 */
|
||||
handleImport() {
|
||||
|
||||
@@ -41,33 +41,33 @@
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="申请编号" align="center" prop="id" />
|
||||
<el-table-column label="状态" align="center" prop="result">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.startTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" align="center" prop="endTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.endTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="请假类型" align="center" prop="type">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="scope.row.type"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="原因" align="center" prop="reason" />
|
||||
<el-table-column label="申请时间" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleCancel(scope.row)"
|
||||
v-hasPermi="['bpm:oa-leave:create']" v-if="scope.row.result === 1">取消请假</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleDetail(scope.row)"
|
||||
@@ -159,7 +159,7 @@ export default {
|
||||
type: 'warning',
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/, // 判断非空,且非空格
|
||||
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
|
||||
inputErrorMessage: "取消原因不能为空",
|
||||
}).then(({ value }) => {
|
||||
return cancelProcessInstance(id, value);
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
<div v-if="!selectProcessInstance">
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="流程名称" align="center" prop="name" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" @click="handleBpmnDetail(scope.row)">
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程分类" align="center" prop="category" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-tag size="medium" v-if="scope.row">v{{ scope.row.version }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程描述" align="center" prop="description" width="300" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" size="small" icon="el-icon-plus" @click="handleSelect(scope.row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -110,8 +110,8 @@ import {DICT_TYPE, getDictDatas} from "@/utils/dict";
|
||||
import store from "@/store";
|
||||
import {decodeFields} from "@/utils/formGenerator";
|
||||
import Parser from '@/components/parser/Parser'
|
||||
import {createProcessInstance, getProcessInstance} from "@/api/bpm/processInstance";
|
||||
import {approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee,backTask} from "@/api/bpm/task";
|
||||
import {getProcessInstance} from "@/api/bpm/processInstance";
|
||||
import {approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee} from "@/api/bpm/task";
|
||||
import {getDate} from "@/utils/dateUtils";
|
||||
import {listSimpleUsers} from "@/api/system/user";
|
||||
import {getActivityList} from "@/api/bpm/activity";
|
||||
|
||||
@@ -52,39 +52,39 @@
|
||||
<el-table-column label="编号" align="center" prop="id" width="320" />
|
||||
<el-table-column label="流程名" align="center" prop="name" />
|
||||
<el-table-column label="流程分类" align="center" prop="category">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前审批任务" align="center" prop="tasks">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button v-for="task in scope.row.tasks" :key="task.id" type="text" @click="handleFormDetail(task.id)">
|
||||
<span>{{ task.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结果" align="center" prop="result">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.endTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" size="small" icon="el-icon-delete" v-if="scope.row.result === 1"
|
||||
v-hasPermi="['bpm:process-instance:cancel']" @click="handleCancel(scope.row)">取消</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)"
|
||||
@@ -164,7 +164,7 @@ export default {
|
||||
type: 'warning',
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/, // 判断非空,且非空格
|
||||
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
|
||||
inputErrorMessage: "取消原因不能为空",
|
||||
}).then(({ value }) => {
|
||||
return cancelProcessInstance(id, value);
|
||||
|
||||
@@ -24,28 +24,28 @@
|
||||
<el-table-column label="所属流程" align="center" prop="processInstance.name" width="200" />
|
||||
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" width="120" />
|
||||
<el-table-column label="结果" align="center" prop="result">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批意见" align="center" prop="reason" width="200" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批时间" align="center" prop="endTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.endTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="耗时" align="center" prop="durationInMillis" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ getDateStar(scope.row.durationInMillis) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
|
||||
v-hasPermi="['bpm:task:query']">详情</el-button>
|
||||
</template>
|
||||
|
||||
@@ -24,18 +24,18 @@
|
||||
<el-table-column label="所属流程" align="center" prop="processInstance.name" />
|
||||
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="version" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
|
||||
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
|
||||
v-hasPermi="['bpm:task:update']">审批</el-button>
|
||||
</template>
|
||||
@@ -50,7 +50,6 @@
|
||||
|
||||
<script>
|
||||
import {getTodoTaskPage} from '@/api/bpm/task'
|
||||
import {listSimpleUsers} from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "Todo",
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
<el-table-column label="任务名" align="center" prop="taskDefinitionName" width="120" fixed />
|
||||
<el-table-column label="任务标识" align="center" prop="taskDefinitionKey" width="120" show-tooltip-when-overflow />
|
||||
<el-table-column label="规则类型" align="center" prop="type" width="120">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规则范围" align="center" prop="options" width="440px">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-tag size="medium" v-if="scope.row.options" :key="option" v-for="option in scope.row.options">
|
||||
{{ getAssignRuleOptionName(scope.row.type, option) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="modelId" label="操作" align="center" width="80" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdateTaskAssignRule(scope.row)"
|
||||
v-hasPermi="['bpm:task-assign-rule:update']">修改</el-button>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import resize from './mixins/resize'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import resize from './mixins/resize'
|
||||
|
||||
|
||||
@@ -143,8 +143,7 @@ export default {
|
||||
.card-panel-description {
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
margin: 26px;
|
||||
margin-left: 0px;
|
||||
margin: 26px 26px 26px 0;
|
||||
|
||||
.card-panel-text {
|
||||
line-height: 18px;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import resize from './mixins/resize'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import resize from './mixins/resize'
|
||||
|
||||
|
||||
@@ -1,570 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container home">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :lg="24">
|
||||
<blockquote class="text-warning" style="font-size: 14px">
|
||||
领取阿里云通用云产品1888优惠券
|
||||
<br />
|
||||
<el-link
|
||||
href="https://www.aliyun.com/minisite/goods?userCode=brki8iof"
|
||||
type="primary"
|
||||
target="_blank"
|
||||
>https://www.aliyun.com/minisite/goods?userCode=brki8iof</el-link
|
||||
>
|
||||
<br />
|
||||
领取腾讯云通用云产品2860优惠券
|
||||
<br />
|
||||
<el-link
|
||||
href="https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console"
|
||||
type="primary"
|
||||
target="_blank"
|
||||
>https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console</el-link
|
||||
>
|
||||
<br />
|
||||
阿里云服务器折扣区
|
||||
<el-link href="http://aly.ruoyi.vip" type="primary" target="_blank"
|
||||
>>☛☛点我进入☚☚</el-link
|
||||
>
|
||||
腾讯云服务器秒杀区
|
||||
<el-link href="http://txy.ruoyi.vip" type="primary" target="_blank"
|
||||
>>☛☛点我进入☚☚</el-link
|
||||
><br />
|
||||
<h4 class="text-danger">
|
||||
云产品通用红包,可叠加官网常规优惠使用。(仅限新用户)
|
||||
</h4>
|
||||
</blockquote>
|
||||
|
||||
<hr />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||
<h2>芋道后台管理框架</h2>
|
||||
<p>
|
||||
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了芋道管理系统。,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
|
||||
</p>
|
||||
<p>
|
||||
<b>当前版本:</b> <span>v{{ version }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<el-tag type="danger">¥免费开源</el-tag>
|
||||
</p>
|
||||
<p>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
icon="el-icon-cloudy"
|
||||
plain
|
||||
@click="goTarget('https://gitee.com/zhijiantianya/ruoyi-vue-pro')"
|
||||
>访问码云</el-button
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
icon="el-icon-s-home"
|
||||
plain
|
||||
@click="goTarget('http://ruoyi.vip')"
|
||||
>访问主页</el-button
|
||||
>
|
||||
</p>
|
||||
</el-col>
|
||||
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 50px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h2>技术选型</h2>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<h4>后端技术</h4>
|
||||
<ul>
|
||||
<li>SpringBoot</li>
|
||||
<li>Spring Security</li>
|
||||
<li>JWT</li>
|
||||
<li>MyBatis</li>
|
||||
<li>Druid</li>
|
||||
<li>Fastjson</li>
|
||||
<li>...</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<h4>前端技术</h4>
|
||||
<ul>
|
||||
<li>Vue</li>
|
||||
<li>Vuex</li>
|
||||
<li>Element-ui</li>
|
||||
<li>Axios</li>
|
||||
<li>Sass</li>
|
||||
<li>Quill</li>
|
||||
<li>...</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="8">
|
||||
<el-card class="update-log">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>联系信息</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p>
|
||||
<i class="el-icon-s-promotion"></i> 官网:<el-link
|
||||
href="http://www.ruoyi.vip"
|
||||
target="_blank"
|
||||
>http://www.ruoyi.vip</el-link
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<i class="el-icon-user-solid"></i> QQ群:<s>满937441</s>
|
||||
<s>满887144332</s> <s>满180251782</s>
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=4WWwqNxN" target="_blank"
|
||||
> 104180207</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<i class="el-icon-chat-dot-round"></i> 微信:<a
|
||||
href="javascript:;"
|
||||
>/ *芋道</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<i class="el-icon-money"></i> 支付宝:<a
|
||||
href="javascript:;"
|
||||
class="支付宝信息"
|
||||
>/ *芋道</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="8">
|
||||
<el-card class="update-log">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>更新日志</span>
|
||||
</div>
|
||||
<el-collapse accordion>
|
||||
<el-collapse-item title="v3.3.0 - 2020-12-14">
|
||||
<ol>
|
||||
<li>新增缓存监控功能</li>
|
||||
<li>支持主题风格配置</li>
|
||||
<li>修复多级菜单之间切换无法缓存的问题</li>
|
||||
<li>多级菜单自动配置组件</li>
|
||||
<li>代码生成预览支持高亮显示</li>
|
||||
<li>支持Get请求映射Params参数</li>
|
||||
<li>删除用户和角色解绑关联</li>
|
||||
<li>去除用户手机邮箱部门必填验证</li>
|
||||
<li>Excel支持注解align对齐方式</li>
|
||||
<li>Excel支持导入Boolean型数据</li>
|
||||
<li>优化头像样式,鼠标移入悬停遮罩</li>
|
||||
<li>代码生成预览提供滚动机制</li>
|
||||
<li>代码生成删除多余的数字float类型</li>
|
||||
<li>修正转换字符串的目标字符集属性</li>
|
||||
<li>回显数据字典防止空值报错</li>
|
||||
<li>日志记录增加过滤多文件场景</li>
|
||||
<li>修改缓存Set方法可能导致嵌套的问题</li>
|
||||
<li>移除前端一些多余的依赖</li>
|
||||
<li>防止安全扫描YUI出现的风险提示</li>
|
||||
<li>修改node-sass为dart-sass</li>
|
||||
<li>升级SpringBoot到最新版本2.1.18</li>
|
||||
<li>升级poi到最新版本4.1.2</li>
|
||||
<li>升级oshi到最新版本v5.3.6</li>
|
||||
<li>升级bitwalker到最新版本1.21</li>
|
||||
<li>升级axios到最新版本0.21.0</li>
|
||||
<li>升级element-ui到最新版本2.14.1</li>
|
||||
<li>升级vue到最新版本2.6.12</li>
|
||||
<li>升级vuex到最新版本3.6.0</li>
|
||||
<li>升级vue-cli到版本4.5.9</li>
|
||||
<li>升级vue-router到最新版本3.4.9</li>
|
||||
<li>升级vue-cli到最新版本4.4.6</li>
|
||||
<li>升级vue-cropper到最新版本0.5.5</li>
|
||||
<li>升级clipboard到最新版本2.0.6</li>
|
||||
<li>升级core-js到最新版本3.8.1</li>
|
||||
<li>升级echarts到最新版本4.9.0</li>
|
||||
<li>升级file-saver到最新版本2.0.4</li>
|
||||
<li>升级fuse.js到最新版本6.4.3</li>
|
||||
<li>升级js-beautify到最新版本1.13.0</li>
|
||||
<li>升级js-cookie到最新版本2.2.1</li>
|
||||
<li>升级path-to-regexp到最新版本6.2.0</li>
|
||||
<li>升级quill到最新版本1.3.7</li>
|
||||
<li>升级screenfull到最新版本5.0.2</li>
|
||||
<li>升级sortablejs到最新版本1.10.2</li>
|
||||
<li>升级vuedraggable到最新版本2.24.3</li>
|
||||
<li>升级chalk到最新版本4.1.0</li>
|
||||
<li>升级eslint到最新版本7.15.0</li>
|
||||
<li>升级eslint-plugin-vue到最新版本7.2.0</li>
|
||||
<li>升级lint-staged到最新版本10.5.3</li>
|
||||
<li>升级runjs到最新版本4.4.2</li>
|
||||
<li>升级sass-loader到最新版本10.1.0</li>
|
||||
<li>升级script-ext-html-webpack-plugin到最新版本2.1.5</li>
|
||||
<li>升级svg-sprite-loader到最新版本5.1.1</li>
|
||||
<li>升级vue-template-compiler到最新版本2.6.12</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.2.1 - 2020-11-18">
|
||||
<ol>
|
||||
<li>阻止任意文件下载漏洞</li>
|
||||
<li>代码生成支持上传控件</li>
|
||||
<li>新增图片上传组件</li>
|
||||
<li>调整默认首页</li>
|
||||
<li>升级druid到最新版本v1.2.2</li>
|
||||
<li>mapperLocations配置支持分隔符</li>
|
||||
<li>权限信息调整</li>
|
||||
<li>调整sql默认时间</li>
|
||||
<li>解决代码生成没有bit类型的问题</li>
|
||||
<li>升级pagehelper到最新版1.3.0</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.2.0 - 2020-10-10">
|
||||
<ol>
|
||||
<li>升级springboot版本到2.1.17 提升安全性</li>
|
||||
<li>升级oshi到最新版本v5.2.5</li>
|
||||
<li>升级druid到最新版本v1.2.1</li>
|
||||
<li>升级jjwt到版本0.9.1</li>
|
||||
<li>升级fastjson到最新版1.2.74</li>
|
||||
<li>修改sass为node-sass,避免el-icon图标乱码</li>
|
||||
<li>代码生成支持同步数据库</li>
|
||||
<li>代码生成支持富文本控件</li>
|
||||
<li>代码生成页面时不忽略remark属性</li>
|
||||
<li>代码生成添加select必填选项</li>
|
||||
<li>Excel导出类型NUMERIC支持精度浮点类型</li>
|
||||
<li>Excel导出targetAttr优化获取值,防止get方法不规范</li>
|
||||
<li>Excel注解支持自动统计数据总和</li>
|
||||
<li>Excel注解支持设置BigDecimal精度&舍入规则</li>
|
||||
<li>菜单&数据权限新增(展开/折叠 全选/全不选 父子联动)</li>
|
||||
<li>允许用户分配到部门父节点</li>
|
||||
<li>菜单新增是否缓存keep-alive</li>
|
||||
<li>表格操作列间距调整</li>
|
||||
<li>限制系统内置参数不允许删除</li>
|
||||
<li>富文本组件优化,支持自定义高度&图片冲突问题</li>
|
||||
<li>富文本工具栏样式对齐</li>
|
||||
<li>导入excel整形值校验优化</li>
|
||||
<li>修复页签关闭所有时固定标签路由不刷新问题</li>
|
||||
<li>表单构建布局型组件新增按钮</li>
|
||||
<li>左侧菜单文字过长显示省略号</li>
|
||||
<li>修正根节点为子部门时,树状结构显示问题</li>
|
||||
<li>修正调用目标字符串最大长度</li>
|
||||
<li>修正菜单提示信息错误</li>
|
||||
<li>修正定时任务执行一次权限标识</li>
|
||||
<li>修正数据库字符串类型nvarchar</li>
|
||||
<li>优化递归子节点</li>
|
||||
<li>优化数据权限判断</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item title="v3.1.0 - 2020-08-13">
|
||||
<ol>
|
||||
<li>表格工具栏右侧添加刷新&显隐查询组件</li>
|
||||
<li>后端支持CORS跨域请求</li>
|
||||
<li>代码生成支持选择上级菜单</li>
|
||||
<li>代码生成支持自定义路径</li>
|
||||
<li>代码生成支持复选框</li>
|
||||
<li>Excel导出导入支持dictType字典类型</li>
|
||||
<li>Excel支持分割字符串组内容</li>
|
||||
<li>验证码类型支持(数组计算、字符验证)</li>
|
||||
<li>升级vue-cli版本到4.4.4</li>
|
||||
<li>修改 node-sass 为 dart-sass</li>
|
||||
<li>表单类型为Integer/Long设置整形默认值</li>
|
||||
<li>代码生成器默认mapper路径与默认mapperScan路径不一致</li>
|
||||
<li>优化防重复提交拦截器</li>
|
||||
<li>优化上级菜单不能选择自己</li>
|
||||
<li>修复角色的权限分配后,未实时生效问题</li>
|
||||
<li>修复在线用户日志记录类型</li>
|
||||
<li>修复富文本空格和缩进保存后不生效问题</li>
|
||||
<li>修复在线用户判断逻辑</li>
|
||||
<li>唯一限制条件只返回单条数据</li>
|
||||
<li>添加获取当前的环境配置方法</li>
|
||||
<li>超时登录后页面跳转到首页</li>
|
||||
<li>全局异常状态汉化拦截处理</li>
|
||||
<li>HTML过滤器改为将html转义</li>
|
||||
<li>检查字符支持小数点&降级改成异常提醒</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item title="v3.0.0 - 2020-07-20">
|
||||
<ol>
|
||||
<li>单应用调整为多模块项目</li>
|
||||
<li>升级element-ui版本到2.13.2</li>
|
||||
<li>删除babel,提高编译速度。</li>
|
||||
<li>新增菜单默认主类目</li>
|
||||
<li>编码文件名修改为uuid方式</li>
|
||||
<li>定时任务cron表达式验证</li>
|
||||
<li>角色权限修改时已有权限未自动勾选异常修复</li>
|
||||
<li>防止切换权限用户后登录出现404</li>
|
||||
<li>Excel支持sort导出排序</li>
|
||||
<li>创建用户不允许选择超级管理员角色</li>
|
||||
<li>修复代码生成导入表结构出现异常页面不提醒问题</li>
|
||||
<li>修复代码生成点击多次表修改数据不变化的问题</li>
|
||||
<li>修复头像上传成功二次打开无法改变裁剪框大小和位置问题</li>
|
||||
<li>修复布局为small者mini用户表单显示错位问题</li>
|
||||
<li>修复热部署导致的强换异常问题</li>
|
||||
<li>修改用户管理复选框宽度,防止部分浏览器出现省略号</li>
|
||||
<li>IpUtils工具,清除Xss特殊字符,防止Xff注入攻击</li>
|
||||
<li>生成domain 如果是浮点型 统一用BigDecimal</li>
|
||||
<li>定时任务调整label-width,防止部署出现错位</li>
|
||||
<li>调整表头固定列默认样式</li>
|
||||
<li>代码生成模板调整,字段为String并且必填则加空串条件</li>
|
||||
<li>代码生成字典Integer/Long使用parseInt</li>
|
||||
<li>
|
||||
修复dict_sort不可update为0的问题&查询返回增加dict_sort升序排序
|
||||
</li>
|
||||
<li>修正岗位导出权限注解</li>
|
||||
<li>禁止加密密文返回前端</li>
|
||||
<li>修复代码生成页面中的查询条件创建时间未生效的问题</li>
|
||||
<li>修复首页搜索菜单外链无法点击跳转问题</li>
|
||||
<li>修复菜单管理选择图标,backspace删除时不过滤数据</li>
|
||||
<li>用户管理部门分支节点不可检查&显示计数</li>
|
||||
<li>数据范围过滤属性调整</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item title="v2.3.0 - 2020-06-01">
|
||||
<ol>
|
||||
<li>升级fastjson到最新版1.2.70 修复高危安全漏洞</li>
|
||||
<li>dev启动默认打开浏览器</li>
|
||||
<li>vue-cli使用默认source-map</li>
|
||||
<li>slidebar eslint报错优化</li>
|
||||
<li>当tags-view滚动关闭右键菜单</li>
|
||||
<li>字典管理添加缓存读取</li>
|
||||
<li>参数管理支持缓存操作</li>
|
||||
<li>支持一级菜单(和主页同级)在main区域显示</li>
|
||||
<li>限制外链地址必须以http(s)开头</li>
|
||||
<li>tagview & sidebar 主题颜色与element ui(全局)同步</li>
|
||||
<li>修改数据源类型优先级,先根据方法,再根据类</li>
|
||||
<li>支持是否需要设置token属性,自定义返回码消息。</li>
|
||||
<li>swagger请求前缀加入配置。</li>
|
||||
<li>登录地点设置内容过长则隐藏显示</li>
|
||||
<li>修复定时任务执行一次按钮后不提示消息问题</li>
|
||||
<li>修改上级部门(选择项排除本身和下级)</li>
|
||||
<li>通用http发送方法增加参数 contentType 编码类型</li>
|
||||
<li>更换IP地址查询接口</li>
|
||||
<li>修复页签变量undefined</li>
|
||||
<li>添加校验部门包含未停用的子部门</li>
|
||||
<li>修改定时任务详情下次执行时间日期显示错误</li>
|
||||
<li>角色管理查询设置默认排序字段</li>
|
||||
<li>swagger添加enable参数控制是否启用</li>
|
||||
<li>只对json类型请求构建可重复读取inputStream的request</li>
|
||||
<li>修改代码生成字典字段int类型没有自动选中问题</li>
|
||||
<li>vuex用户名取值修正</li>
|
||||
<li>表格树模板去掉多余的)</li>
|
||||
<li>代码生成序号修正</li>
|
||||
<li>全屏情况下不调整上外边距</li>
|
||||
<li>代码生成Date字段添加默认格式</li>
|
||||
<li>用户管理角色选择权限控制</li>
|
||||
<li>修复路由懒加载报错问题</li>
|
||||
<li>模板sql.vm添加菜单状态</li>
|
||||
<li>设置用户名称不能修改</li>
|
||||
<li>dialog添加append-to-body属性,防止ie遮罩</li>
|
||||
<li>菜单区分状态和显示隐藏功能</li>
|
||||
<li>升级fastjson到最新版1.2.68 修复安全加固</li>
|
||||
<li>修复代码生成如果选择字典类型缺失逗号问题</li>
|
||||
<li>登录请求params更换为data,防止暴露url</li>
|
||||
<li>日志返回时间格式处理</li>
|
||||
<li>添加handle控制允许拖动的元素</li>
|
||||
<li>布局设置点击扩大范围</li>
|
||||
<li>代码生成列属性排序查询</li>
|
||||
<li>代码生成列支持拖动排序</li>
|
||||
<li>修复时间格式不支持ios问题</li>
|
||||
<li>表单构建添加父级class,防止冲突</li>
|
||||
<li>定时任务并发属性修正</li>
|
||||
<li>角色禁用&菜单隐藏不查询权限</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item title="v2.2.0 - 2020-03-18">
|
||||
<ol>
|
||||
<li>系统监控新增定时任务功能</li>
|
||||
<li>添加一个打包Web工程bat</li>
|
||||
<li>修复页签鼠标滚轮按下的时候,可以关闭不可关闭的tag</li>
|
||||
<li>修复点击退出登录有时会无提示问题</li>
|
||||
<li>修复防重复提交注解无效问题</li>
|
||||
<li>修复通知公告批量删除异常问题</li>
|
||||
<li>添加菜单时路由地址必填限制</li>
|
||||
<li>代码生成字段描述可编辑</li>
|
||||
<li>修复用户修改个人信息导致缓存不过期问题</li>
|
||||
<li>个人信息创建时间获取正确属性值</li>
|
||||
<li>操作日志详细显示正确类型</li>
|
||||
<li>导入表单击行数据时选中对应的复选框</li>
|
||||
<li>批量替换表前缀逻辑调整</li>
|
||||
<li>固定重定向路径表达式</li>
|
||||
<li>升级element-ui版本到2.13.0</li>
|
||||
<li>操作日志排序调整</li>
|
||||
<li>修复charts切换侧边栏或者缩放窗口显示bug</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item title="v2.1.0 - 2020-02-24">
|
||||
<ol>
|
||||
<li>新增表单构建</li>
|
||||
<li>代码生成支持树表结构</li>
|
||||
<li>新增用户导入</li>
|
||||
<li>修复动态加载路由页面刷新问题</li>
|
||||
<li>修复地址开关无效问题</li>
|
||||
<li>汉化错误提示页面</li>
|
||||
<li>代码生成已知问题修改</li>
|
||||
<li>修复多数据源下配置关闭出现异常处理</li>
|
||||
<li>添加HTML过滤器,用于去除XSS漏洞隐患</li>
|
||||
<li>修复上传头像控制台出现异常</li>
|
||||
<li>修改用户管理分页不正确的问题</li>
|
||||
<li>修复验证码记录提示错误</li>
|
||||
<li>修复request.js缺少Message引用</li>
|
||||
<li>修复表格时间为空出现的异常</li>
|
||||
<li>添加Jackson日期反序列化时区配置</li>
|
||||
<li>调整根据用户权限加载菜单数据树形结构</li>
|
||||
<li>调整成功登录不恢复按钮,防止多次点击</li>
|
||||
<li>修改用户个人资料同步缓存信息</li>
|
||||
<li>修复页面同时出现el-upload和Editor不显示处理</li>
|
||||
<li>修复在角色管理页修改菜单权限偶尔未选中问题</li>
|
||||
<li>配置文件新增redis密码属性</li>
|
||||
<li>设置mybatis全局的配置文件</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item title="v2.0.0 - 2019-12-02">
|
||||
<ol>
|
||||
<li>新增代码生成</li>
|
||||
<li>新增@RepeatSubmit注解,防止重复提交</li>
|
||||
<li>新增菜单主目录添加/删除操作</li>
|
||||
<li>日志记录过滤特殊对象,防止转换异常</li>
|
||||
<li>修改代码生成路由脚本错误</li>
|
||||
<li>用户上传头像实时同步缓存,无需重新登录</li>
|
||||
<li>调整切换页签后不重新加载数据</li>
|
||||
<li>添加jsencrypt实现参数的前端加密</li>
|
||||
<li>系统退出删除用户缓存记录</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v1.1.0 - 2019-11-11">
|
||||
<ol>
|
||||
<li>新增在线用户管理</li>
|
||||
<li>新增按钮组功能实现(批量删除、导出、清空)</li>
|
||||
<li>新增查询条件重置按钮</li>
|
||||
<li>新增Swagger全局Token配置</li>
|
||||
<li>新增后端参数校验</li>
|
||||
<li>修复字典管理页面的日期查询异常</li>
|
||||
<li>修改时间函数命名防止冲突</li>
|
||||
<li>去除菜单上级校验,默认为顶级</li>
|
||||
<li>修复用户密码无法修改问题</li>
|
||||
<li>修复菜单类型为按钮时不显示权限标识</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v1.0.0 - 2019-10-08">
|
||||
<ol>
|
||||
<li>芋道前后端分离系统正式发布</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="8">
|
||||
<el-card class="update-log">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>捐赠支持</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
<img
|
||||
src="https://oscimg.oschina.net/oscnet/up-d6695f82666e5018f715c41cb7ee60d3b73.png"
|
||||
alt="donate"
|
||||
width="100%"
|
||||
/>
|
||||
<span style="display: inline-block; height: 30px; line-height: 30px"
|
||||
>你可以请作者喝杯咖啡表示鼓励</span
|
||||
>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Index",
|
||||
data() {
|
||||
return {
|
||||
// 版本号
|
||||
version: "3.3.0",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
goTarget(href) {
|
||||
window.open(href, "_blank");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.home {
|
||||
blockquote {
|
||||
padding: 10px 20px;
|
||||
margin: 0 0 20px;
|
||||
font-size: 17.5px;
|
||||
border-left: 5px solid #eee;
|
||||
}
|
||||
hr {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.col-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #676a6c;
|
||||
overflow-x: hidden;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
font-size: 26px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
|
||||
b {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.update-log {
|
||||
ol {
|
||||
display: block;
|
||||
list-style-type: decimal;
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
padding-inline-start: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<el-table-column label="日志编号" align="center" prop="id" />
|
||||
<el-table-column label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column label="用户类型" align="center" prop="userType">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>
|
||||
</template>
|
||||
</el-table-column>>
|
||||
@@ -56,22 +56,22 @@
|
||||
<el-table-column label="请求方法名" align="center" prop="requestMethod" />
|
||||
<el-table-column label="请求地址" align="center" prop="requestUrl" width="250" />
|
||||
<el-table-column label="请求时间" align="center" prop="beginTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.beginTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="执行时长" align="center" prop="startTime">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ scope.row.duration }} ms</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作结果" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ scope.row.resultCode === 0 ? '成功' : '失败(' + scope.row.resultMsg + ')' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)"
|
||||
v-hasPermi="['infra:api-access-log:query']">详细</el-button>
|
||||
</template>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<el-table-column label="日志编号" align="center" prop="id" />
|
||||
<el-table-column label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column label="用户类型" align="center" prop="userType">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>
|
||||
</template>
|
||||
</el-table-column>>
|
||||
@@ -56,18 +56,18 @@
|
||||
<el-table-column label="请求方法名" align="center" prop="requestMethod" />
|
||||
<el-table-column label="请求地址" align="center" prop="requestUrl" width="250" />
|
||||
<el-table-column label="异常发生时间" align="center" prop="exceptionTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.exceptionTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="异常名" align="center" prop="exceptionName" width="250" />
|
||||
<el-table-column label="处理状态" align="center" prop="processStatus">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS" :value="scope.row.processStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)"
|
||||
v-hasPermi="['infra:api-access-log:query']">详细</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-check"
|
||||
|
||||
@@ -84,7 +84,7 @@ import {
|
||||
} from '@/components/generator/html'
|
||||
import { makeUpJs } from '@/components/generator/js'
|
||||
import { makeUpCss } from '@/components/generator/css'
|
||||
import { exportDefault, beautifierConf, titleCase } from '@/utils/index'
|
||||
import { exportDefault, beautifierConf } from '@/utils'
|
||||
import ResourceDialog from './ResourceDialog'
|
||||
import loadMonaco from '@/utils/loadMonaco'
|
||||
import loadBeautifier from '@/utils/loadBeautifier'
|
||||
@@ -325,7 +325,7 @@ export default {
|
||||
}
|
||||
}
|
||||
@include action-bar;
|
||||
::v-deep .el-drawer__header {
|
||||
:deep(.el-drawer__header) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -101,7 +101,7 @@ export default {
|
||||
}
|
||||
}
|
||||
.icon-dialog {
|
||||
::v-deep .el-dialog {
|
||||
:deep(.el-dialog) {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 0;
|
||||
margin-top: 4vh !important;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { beautifierConf } from '@/utils/index'
|
||||
import { beautifierConf } from '@/utils'
|
||||
import ClipboardJS from 'clipboard'
|
||||
import { saveAs } from 'file-saver'
|
||||
import loadMonaco from '@/utils/loadMonaco'
|
||||
@@ -133,7 +133,7 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/mixin.scss';
|
||||
|
||||
::v-deep .el-drawer__header {
|
||||
:deep(.el-drawer__header) {
|
||||
display: none;
|
||||
}
|
||||
@include action-bar;
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { deepClone } from '@/utils/index'
|
||||
import { deepClone } from '@/utils'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
|
||||
@@ -651,10 +651,10 @@
|
||||
<script>
|
||||
import { isArray } from 'util'
|
||||
import TreeNodeDialog from './TreeNodeDialog'
|
||||
import { isNumberStr } from '@/utils/index'
|
||||
import { isNumberStr } from '@/utils'
|
||||
import IconsDialog from './IconsDialog'
|
||||
import {
|
||||
inputComponents, selectComponents, layoutComponents
|
||||
inputComponents, selectComponents
|
||||
} from '@/components/generator/config'
|
||||
import { saveFormConf } from '@/utils/db'
|
||||
|
||||
@@ -1021,7 +1021,7 @@ export default {
|
||||
.el-date-editor {
|
||||
width: 227px;
|
||||
}
|
||||
::v-deep .el-icon-time {
|
||||
:deep(.el-icon-time) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { isNumberStr } from '@/utils/index'
|
||||
import { isNumberStr } from '@/utils'
|
||||
import { getTreeNodeId, saveTreeNodeId } from '@/utils/db'
|
||||
|
||||
const id = getTreeNodeId()
|
||||
|
||||
@@ -133,8 +133,8 @@ import {
|
||||
inputComponents, selectComponents, layoutComponents, formConf
|
||||
} from '@/components/generator/config'
|
||||
import {
|
||||
exportDefault, beautifierConf, isNumberStr, titleCase, deepClone, isObjectObject
|
||||
} from '@/utils/index'
|
||||
beautifierConf, titleCase, deepClone
|
||||
} from '@/utils'
|
||||
import {
|
||||
makeUpHtml, vueTemplate, vueScript, cssStyle
|
||||
} from '@/components/generator/html'
|
||||
@@ -272,7 +272,7 @@ export default {
|
||||
arr.reduce((pre, item, i) => {
|
||||
if (arr.length === i + 1) {
|
||||
pre[item] = data
|
||||
} else if (!isObjectObject(pre[item])) {
|
||||
} else if (pre[item]===undefined) {
|
||||
pre[item] = {}
|
||||
}
|
||||
return pre[item]
|
||||
@@ -300,7 +300,7 @@ export default {
|
||||
url
|
||||
}).then(resp => {
|
||||
this.setLoading(component, false)
|
||||
this.setRespData(component, resp.data)
|
||||
this.setRespData(component, resp)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from '@/router'
|
||||
import '@/styles/index.scss'
|
||||
import '@/icons'
|
||||
import '@/assets/icons'
|
||||
import axios from 'axios'
|
||||
import Tinymce from '@/components/tinymce/index.vue'
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="字段描述" min-width="10%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-input v-model="scope.row.columnComment"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -24,45 +24,45 @@
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="Java类型" min-width="11%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-select v-model="scope.row.javaType">
|
||||
<el-option label="Long" value="Long" />
|
||||
<el-option label="String" value="String" />
|
||||
<el-option label="Integer" value="Integer" />
|
||||
<el-option label="Double" value="Double" />
|
||||
<el-option label="BigDecimal" value="BigDecimal" />
|
||||
<el-option label="Date" value="Date" />
|
||||
<el-option label="LocalDateTime" value="LocalDateTime" />
|
||||
<el-option label="Boolean" value="Boolean" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="java属性" min-width="10%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-input v-model="scope.row.javaField"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="插入" min-width="4%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-checkbox true-label="true" false-label="false" v-model="scope.row.createOperation"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="编辑" min-width="4%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-checkbox true-label="true" false-label="false" v-model="scope.row.updateOperation"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="列表" min-width="4%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-checkbox true-label="true" false-label="false" v-model="scope.row.listOperationResult"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="查询" min-width="4%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-checkbox true-label="true" false-label="false" v-model="scope.row.listOperation"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="查询方式" min-width="10%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-select v-model="scope.row.listOperationCondition">
|
||||
<el-option label="=" value="=" />
|
||||
<el-option label="!=" value="!=" />
|
||||
@@ -76,12 +76,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="允许空" min-width="5%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-checkbox true-label="true" false-label="false" v-model="scope.row.nullable"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="显示类型" min-width="12%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-select v-model="scope.row.htmlType">
|
||||
<el-option label="文本框" value="input" />
|
||||
<el-option label="文本域" value="textarea" />
|
||||
@@ -96,7 +96,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字典类型" min-width="12%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择">
|
||||
<el-option
|
||||
v-for="dict in dictOptions"
|
||||
@@ -108,7 +108,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="示例" min-width="10%">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-input v-model="scope.row.example"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" v-if="info.genType == '1'">
|
||||
<el-col :span="24" v-if="info.genType === '1'">
|
||||
<el-form-item prop="genPath">
|
||||
<span slot="label">
|
||||
自定义路径
|
||||
@@ -128,7 +128,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row v-show="info.tplCategory == 'tree'">
|
||||
<el-row v-show="info.tplCategory === 'tree'">
|
||||
<h4 class="form-header">其他信息</h4>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
@@ -185,7 +185,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-show="info.tplCategory == 'sub'">
|
||||
<el-row v-show="info.tplCategory === 'sub'">
|
||||
<h4 class="form-header">关联信息</h4>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
@@ -314,7 +314,7 @@ export default {
|
||||
},
|
||||
/** 设置关联外键 */
|
||||
setSubTableColumns(value) {
|
||||
for (var item in this.tables) {
|
||||
for (let item in this.tables) {
|
||||
const name = this.tables[item].tableName;
|
||||
if (value === name) {
|
||||
this.subColumns = this.tables[item].columns;
|
||||
|
||||
@@ -38,17 +38,17 @@
|
||||
<el-table-column label="表描述" align="center" prop="tableComment" :show-overflow-tooltip="true" width="120"/>
|
||||
<el-table-column label="实体" align="center" prop="className" width="200"/>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.updateTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="300px" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" size="small" icon="el-icon-view" @click="handlePreview(scope.row)" v-hasPermi="['infra:codegen:preview']">预览</el-button>
|
||||
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEditTable(scope.row)" v-hasPermi="['infra:codegen:update']">编辑</el-button>
|
||||
<el-button type="text" size="small" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['infra:codegen:delete']">删除</el-button>
|
||||
@@ -209,7 +209,7 @@ export default {
|
||||
highlightedCode(item) {
|
||||
// const vmName = key.substring(key.lastIndexOf("/") + 1, key.indexOf(".vm"));
|
||||
// var language = vmName.substring(vmName.indexOf(".") + 1, vmName.length);
|
||||
var language = item.filePath.substring(item.filePath.lastIndexOf(".") + 1);
|
||||
const language = item.filePath.substring(item.filePath.lastIndexOf('.') + 1)
|
||||
const result = hljs.highlight(language, item.code || "", true);
|
||||
return result.value || ' ';
|
||||
},
|
||||
|
||||
@@ -41,28 +41,28 @@
|
||||
|
||||
<el-table v-loading="loading" :data="configList">
|
||||
<el-table-column label="参数主键" align="center" prop="id" />
|
||||
<el-table-column label="参数分类" align="center" prop="group" />
|
||||
<el-table-column label="参数分类" align="center" prop="category" />
|
||||
<el-table-column label="参数名称" align="center" prop="name" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="参数键名" align="center" prop="key" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="参数键值" align="center" prop="value" />
|
||||
<el-table-column label="系统内置" align="center" prop="type">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_CONFIG_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否可见" align="center" prop="visible">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ scope.row.visible ? '是' : '否' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['infra:config:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
<el-table-column label="数据源连接" align="center" prop="url" />
|
||||
<el-table-column label="用户名" align="center" prop="username" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['infra:data-source-config:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<doc-alert title="上传下载" url="https://doc.iocoder.cn/file/" />
|
||||
<doc-alert title="上传下载" url="https://doc.iocoder.cn/file/"/>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="文件路径" prop="path">
|
||||
<el-input v-model="queryParams.path" placeholder="请输入文件路径" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="['00:00:00', '23:59:59']"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
@@ -32,9 +34,12 @@
|
||||
<el-table-column label="文件大小" align="center" prop="size" min-width="120px" :formatter="sizeFormat"/>
|
||||
<el-table-column label="文件类型" :show-overflow-tooltip="true" align="center" prop="type" width="180px"/>
|
||||
<el-table-column label="文件内容" align="center" prop="content" min-width="150px">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<image-preview v-if="scope.row.type&&scope.row.type.indexOf('image/') === 0" :src="scope.row.url"
|
||||
:width="'100px'"></image-preview>
|
||||
<video v-else-if="scope.row.type&&scope.row.type.indexOf('video/') === 0" :width="'100px'">
|
||||
<source :src="scope.row.url"/>
|
||||
</video>
|
||||
<i v-else>无法预览,点击
|
||||
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" target="_blank"
|
||||
:href="getFileUrl + scope.row.configId + '/get/' + scope.row.path">下载
|
||||
@@ -43,12 +48,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" min-width="170px">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="100px">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['infra:file:delete']">删除
|
||||
</el-button>
|
||||
@@ -118,7 +123,7 @@ export default {
|
||||
title: "", // 弹出层标题
|
||||
isUploading: false, // 是否禁用上传
|
||||
url: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
headers: {Authorization: "Bearer " + getAccessToken()}, // 设置上传的请求头部
|
||||
data: {} // 上传的额外数据,用于文件名
|
||||
},
|
||||
};
|
||||
@@ -189,19 +194,20 @@ export default {
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除文件编号为"' + id + '"的数据项?').then(function() {
|
||||
this.$modal.confirm('是否确认删除文件编号为"' + id + '"的数据项?').then(function () {
|
||||
return deleteFile(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
// 用户昵称展示
|
||||
sizeFormat(row, column) {
|
||||
const unitArr = ["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"];
|
||||
const unitArr = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
const srcSize = parseFloat(row.size);
|
||||
const index = Math.floor(Math.log(srcSize) / Math.log(1024));
|
||||
let size =srcSize/Math.pow(1024,index);
|
||||
let size = srcSize / Math.pow(1024, index);
|
||||
size = size.toFixed(2);//保留的小数位数
|
||||
return size + ' ' + unitArr[index];
|
||||
},
|
||||
|
||||
@@ -36,23 +36,23 @@
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="配置名" align="center" prop="name" />
|
||||
<el-table-column label="存储器" align="center" prop="storage">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_FILE_STORAGE" :value="scope.row.storage" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="主配置" align="center" prop="primary">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['infra:file-config:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-attract" @click="handleMaster(scope.row)"
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<el-table-column label="任务编号" align="center" prop="id" />
|
||||
<el-table-column label="任务名称" align="center" prop="name" />
|
||||
<el-table-column label="任务状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>>
|
||||
@@ -51,7 +51,7 @@
|
||||
<el-table-column label="处理器的参数" align="center" prop="handlerParam" />
|
||||
<el-table-column label="CRON 表达式" align="center" prop="cronExpression" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['infra:job:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-check" @click="handleChangeStatus(scope.row, true)"
|
||||
@@ -62,9 +62,7 @@
|
||||
v-hasPermi="['infra:job:delete']">删除</el-button>
|
||||
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)"
|
||||
v-hasPermi="['infra:job:trigger', 'infra:job:query']">
|
||||
<span class="el-dropdown-link">
|
||||
<i class="el-icon-d-arrow-right el-icon--right"></i>更多
|
||||
</span>
|
||||
<el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="handleRun" icon="el-icon-caret-right"
|
||||
v-hasPermi="['infra:job:trigger']">执行一次</el-dropdown-item>
|
||||
|
||||
@@ -41,22 +41,22 @@
|
||||
<el-table-column label="处理器的参数" align="center" prop="handlerParam" />
|
||||
<el-table-column label="第几次执行" align="center" prop="executeIndex" />
|
||||
<el-table-column label="执行时间" align="center" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.beginTime) + ' ~ ' + parseTime(scope.row.endTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="执行时长" align="center" prop="duration">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ scope.row.duration + ' 毫秒' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_JOB_LOG_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)" :loading="exportLoading"
|
||||
v-hasPermi="['infra:job:query']">详细</el-button>
|
||||
</template>
|
||||
@@ -77,7 +77,7 @@
|
||||
<el-form-item label="处理器的参数:">{{ form.handlerParam }}</el-form-item>
|
||||
<el-form-item label="第几次执行:">{{ form.executeIndex }}</el-form-item>
|
||||
<el-form-item label="执行时间:">{{ parseTime(form.beginTime) + ' ~ ' + parseTime(form.endTime) }}</el-form-item>
|
||||
<el-form-item label="执行时长:">{{ parseTime(form.duration) + ' 毫秒' }}</el-form-item>
|
||||
<el-form-item label="执行时长:">{{ form.duration + ' 毫秒' }}</el-form-item>
|
||||
<el-form-item label="任务状态:">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_JOB_LOG_STATUS" :value="form.status" />
|
||||
</el-form-item>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<td><div class="cell">Redis版本</div></td>
|
||||
<td><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td>
|
||||
<td><div class="cell">运行模式</div></td>
|
||||
<td><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }}</div></td>
|
||||
<td><div class="cell" v-if="cache.info">{{ cache.info.redis_mode === "standalone" ? "单机" : "集群" }}</div></td>
|
||||
<td><div class="cell">端口</div></td>
|
||||
<td><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td>
|
||||
<td><div class="cell">客户端数</div></td>
|
||||
@@ -71,7 +71,7 @@
|
||||
<el-table-column prop="keyType" label="Key 类型" width="100" />
|
||||
<el-table-column prop="valueType" label="Value 类型" />
|
||||
<el-table-column prop="timeoutType" label="超时时间" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag
|
||||
:type="DICT_TYPE.INFRA_REDIS_TIMEOUT_TYPE"
|
||||
:value="scope.row.timeoutType"
|
||||
@@ -95,10 +95,10 @@
|
||||
</div>
|
||||
<el-table :data="cacheKeys" style="width: 100%" @row-click="handleKeyValue">
|
||||
<el-table-column label="缓存键名" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">{{ scope.row }}</template>
|
||||
<template v-slot="scope">{{ scope.row }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="60" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeleteKey(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -136,8 +136,8 @@
|
||||
|
||||
<script>
|
||||
import {getCache, getKeyDefineList, getKeyList, getKeyValue, deleteKey, deleteKeys} from "@/api/infra/redis";
|
||||
import echarts from "echarts";
|
||||
|
||||
import * as echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
export default {
|
||||
name: "Server",
|
||||
data () {
|
||||
|
||||
@@ -54,12 +54,12 @@
|
||||
<el-table-column label="分类" align="center" prop="category" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['infra:test-demo:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
|
||||
92
yudao-ui-admin/src/views/infra/webSocket/index.vue
Normal file
92
yudao-ui-admin/src/views/infra/webSocket/index.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-width="120px">
|
||||
<el-row type="flex" :gutter="0">
|
||||
<el-col :sm="12">
|
||||
<el-form-item label="WebSocket地址" size="small">
|
||||
<el-input v-model="url" type="text"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :offset="1">
|
||||
<el-form-item label="" label-width="0px" size="small">
|
||||
<el-button @click="connect" type="primary" :disabled="ws&&ws.readyState===1">
|
||||
{{ ws && ws.readyState === 1 ? "已连接" : "连接" }}
|
||||
</el-button>
|
||||
<el-button @click="exit" type="danger">断开</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="发送内容" size="small">
|
||||
<el-input type="textarea" v-model="message" :rows="5"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="" size="small">
|
||||
<el-button type="success" @click="send">发送消息</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="接收内容" size="small">
|
||||
<el-input type="textarea" v-model="content" :rows="12" disabled/>
|
||||
</el-form-item>
|
||||
<el-form-item label="" size="small">
|
||||
<el-button type="info" @click="content=''">清空消息</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from "@/store";
|
||||
import {getNowDateTime} from "@/utils/ruoyi";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
url: process.env.VUE_APP_BASE_API + "/websocket/message",
|
||||
message: "",
|
||||
content: "",
|
||||
ws: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.url = this.url.replace("http", "ws")
|
||||
},
|
||||
methods: {
|
||||
connect() {
|
||||
if (!'WebSocket' in window) {
|
||||
this.$modal.msgError("您的浏览器不支持WebSocket");
|
||||
return;
|
||||
}
|
||||
const userId = store.getters.userId;
|
||||
this.ws = new WebSocket(this.url + "?userId=" + userId);
|
||||
const self = this;
|
||||
this.ws.onopen = function (event) {
|
||||
self.content = self.content + "\n**********************连接开始**********************\n";
|
||||
};
|
||||
this.ws.onmessage = function (event) {
|
||||
self.content = self.content + "接收时间:" + getNowDateTime() + "\n" + event.data + "\n";
|
||||
};
|
||||
this.ws.onclose = function (event) {
|
||||
self.content = self.content + "**********************连接关闭**********************\n";
|
||||
};
|
||||
this.ws.error = function (event) {
|
||||
self.content = self.content + "**********************连接异常**********************\n";
|
||||
};
|
||||
},
|
||||
exit() {
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
},
|
||||
send() {
|
||||
if (!this.ws || this.ws.readyState !== 1) {
|
||||
this.$modal.msgError("未连接到服务器");
|
||||
return;
|
||||
}
|
||||
if (!this.message) {
|
||||
this.$modal.msgError("请输入发送内容");
|
||||
return;
|
||||
}
|
||||
this.ws.send(this.message);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template xmlns="">
|
||||
<div class="container">
|
||||
<div class="logo"></div>
|
||||
<!-- 登录区域 -->
|
||||
@@ -36,19 +36,10 @@
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码"
|
||||
@keyup.enter.native="handleLogin">
|
||||
@keyup.enter.native="getCode">
|
||||
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon"/>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" v-if="captchaEnable">
|
||||
<el-input v-model="loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%"
|
||||
@keyup.enter.native="handleLogin">
|
||||
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon"/>
|
||||
</el-input>
|
||||
<div class="login-code">
|
||||
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-checkbox v-model="loginForm.rememberMe" style="margin:0 0 25px 0;">记住密码</el-checkbox>
|
||||
</div>
|
||||
|
||||
@@ -62,10 +53,10 @@
|
||||
<el-form-item prop="mobileCode">
|
||||
<el-input v-model="loginForm.mobileCode" type="text" auto-complete="off" placeholder="短信验证码"
|
||||
@keyup.enter.native="handleLogin">
|
||||
<template slot="icon">
|
||||
<template v-slot="icon">
|
||||
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon"/>
|
||||
</template>
|
||||
<template slot="append">
|
||||
<template v-slot="append">
|
||||
<span v-if="mobileCodeTimer <= 0" class="getMobileCode" @click="getSmsCode" style="cursor: pointer;">获取验证码</span>
|
||||
<span v-if="mobileCodeTimer > 0" class="getMobileCode">{{ mobileCodeTimer }}秒后可重新获取</span>
|
||||
</template>
|
||||
@@ -76,7 +67,7 @@
|
||||
<!-- 下方的登录按钮 -->
|
||||
<el-form-item style="width:100%;">
|
||||
<el-button :loading="loading" size="medium" type="primary" style="width:100%;"
|
||||
@click.native.prevent="handleLogin">
|
||||
@click.native.prevent="getCode">
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
</el-button>
|
||||
@@ -96,6 +87,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图形验证码 -->
|
||||
<Verify ref="verify" :captcha-type="'blockPuzzle'" :img-size="{width:'400px',height:'200px'}"
|
||||
@success="handleLogin" />
|
||||
|
||||
<!-- footer -->
|
||||
<div class="footer">
|
||||
Copyright © 2020-2022 iocoder.cn All Rights Reserved.
|
||||
@@ -104,10 +100,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getCodeImg, sendSmsCode, socialAuthRedirect} from "@/api/login";
|
||||
import {sendSmsCode, socialAuthRedirect} from "@/api/login";
|
||||
import {getTenantIdByName} from "@/api/system/tenant";
|
||||
import {SystemUserSocialTypeEnum} from "@/utils/constants";
|
||||
import {getTenantEnable} from "@/utils/ruoyi";
|
||||
import {getCaptchaEnable, getTenantEnable} from "@/utils/ruoyi";
|
||||
import {
|
||||
getPassword,
|
||||
getRememberMe, getTenantName,
|
||||
@@ -118,8 +114,14 @@ import {
|
||||
setUsername
|
||||
} from "@/utils/auth";
|
||||
|
||||
import Verify from '@/components/Verifition/Verify';
|
||||
import {resetUserPwd} from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
components: {
|
||||
Verify
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
codeUrl: "",
|
||||
@@ -130,11 +132,10 @@ export default {
|
||||
loginType: "uname",
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
captchaVerification: "",
|
||||
mobile: "",
|
||||
mobileCode: "",
|
||||
rememberMe: false,
|
||||
code: "",
|
||||
uuid: "",
|
||||
tenantName: "芋道源码",
|
||||
},
|
||||
scene: 21,
|
||||
@@ -146,12 +147,11 @@ export default {
|
||||
password: [
|
||||
{required: true, trigger: "blur", message: "密码不能为空"}
|
||||
],
|
||||
code: [{required: true, trigger: "change", message: "验证码不能为空"}],
|
||||
mobile: [
|
||||
{required: true, trigger: "blur", message: "手机号不能为空"},
|
||||
{
|
||||
validator: function (rule, value, callback) {
|
||||
if (/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/.test(value) === false) {
|
||||
if (/^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/.test(value) === false) {
|
||||
callback(new Error("手机号格式错误"));
|
||||
} else {
|
||||
callback();
|
||||
@@ -185,37 +185,34 @@ export default {
|
||||
SysUserSocialTypeEnum: SystemUserSocialTypeEnum,
|
||||
};
|
||||
},
|
||||
// watch: {
|
||||
// $route: {
|
||||
// handler: function(route) {
|
||||
// this.redirect = route.query && route.query.redirect;
|
||||
// },
|
||||
// immediate: true
|
||||
// }
|
||||
// },
|
||||
created() {
|
||||
// 租户开关
|
||||
this.tenantEnable = getTenantEnable();
|
||||
if (this.tenantEnable) {
|
||||
getTenantIdByName(this.loginForm.tenantName).then(res => { // 设置租户
|
||||
const tenantId = res.data;
|
||||
if (tenantId && tenantId >= 0) {
|
||||
setTenantId(tenantId)
|
||||
}
|
||||
});
|
||||
}
|
||||
// 验证码开关
|
||||
this.captchaEnable = getCaptchaEnable();
|
||||
// 重定向地址
|
||||
this.redirect = this.$route.query.redirect;
|
||||
this.getCode();
|
||||
this.redirect = this.$route.query.redirect ? decodeURIComponent(this.$route.query.redirect) : undefined;
|
||||
this.getCookie();
|
||||
},
|
||||
methods: {
|
||||
getCode() {
|
||||
// 只有开启的状态,才加载验证码。默认开启
|
||||
// 情况一,未开启:则直接登录
|
||||
if (!this.captchaEnable) {
|
||||
this.handleLogin({})
|
||||
return;
|
||||
}
|
||||
// 请求远程,获得验证码
|
||||
getCodeImg().then(res => {
|
||||
res = res.data;
|
||||
this.captchaEnable = res.enable;
|
||||
if (this.captchaEnable) {
|
||||
this.codeUrl = "data:image/gif;base64," + res.img;
|
||||
this.loginForm.uuid = res.uuid;
|
||||
}
|
||||
});
|
||||
|
||||
// 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录
|
||||
// 弹出验证码
|
||||
this.$refs.verify.show()
|
||||
},
|
||||
getCookie() {
|
||||
const username = getUsername();
|
||||
@@ -230,7 +227,7 @@ export default {
|
||||
tenantName: tenantName ? tenantName : this.loginForm.tenantName,
|
||||
};
|
||||
},
|
||||
handleLogin() {
|
||||
handleLogin(captchaParams) {
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
@@ -246,6 +243,7 @@ export default {
|
||||
removeRememberMe()
|
||||
removeTenantName()
|
||||
}
|
||||
this.loginForm.captchaVerification = captchaParams.captchaVerification
|
||||
// 发起登陆
|
||||
// console.log("发起登录", this.loginForm);
|
||||
this.$store.dispatch(this.loginForm.loginType === "sms" ? "SmsLogin" : "Login", this.loginForm).then(() => {
|
||||
@@ -253,23 +251,44 @@ export default {
|
||||
});
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
this.getCode();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
doSocialLogin(socialTypeEnum) {
|
||||
async doSocialLogin(socialTypeEnum) {
|
||||
// 设置登录中
|
||||
this.loading = true;
|
||||
// 计算 redirectUri
|
||||
const redirectUri = location.origin + '/social-login?type=' + socialTypeEnum.type + '&redirect=' + (this.redirect || "/"); // 重定向不能丢
|
||||
// const redirectUri = 'http://127.0.0.1:48080/api/gitee/callback';
|
||||
// const redirectUri = 'http://127.0.0.1:48080/api/dingtalk/callback';
|
||||
// 进行跳转
|
||||
socialAuthRedirect(socialTypeEnum.type, encodeURIComponent(redirectUri)).then((res) => {
|
||||
// console.log(res.url);
|
||||
window.location.href = res.data;
|
||||
});
|
||||
let tenant = false;
|
||||
if (this.tenantEnable) {
|
||||
await this.$prompt('请输入租户名称', "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(({value}) => {
|
||||
getTenantIdByName(value).then(res => {
|
||||
const tenantId = res.data;
|
||||
tenant = true
|
||||
if (tenantId && tenantId >= 0) {
|
||||
setTenantId(tenantId)
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
return false
|
||||
});
|
||||
} else {
|
||||
tenant = true
|
||||
}
|
||||
if(tenant){
|
||||
// 计算 redirectUri
|
||||
const redirectUri = location.origin + '/social-login?'
|
||||
+ encodeURIComponent('type=' + socialTypeEnum.type + '&redirect=' + (this.redirect || "/")); // 重定向不能丢
|
||||
// const redirectUri = 'http://127.0.0.1:48080/api/gitee/callback';
|
||||
// const redirectUri = 'http://127.0.0.1:48080/api/dingtalk/callback';
|
||||
// 进行跳转
|
||||
socialAuthRedirect(socialTypeEnum.type, encodeURIComponent(redirectUri)).then((res) => {
|
||||
// console.log(res.url);
|
||||
window.location.href = res.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
/** ========== 以下为升级短信登录 ========== */
|
||||
getSmsCode() {
|
||||
@@ -297,7 +316,7 @@ export default {
|
||||
|
||||
.oauth-login {
|
||||
display: flex;
|
||||
align-items: cen;
|
||||
align-items: center;
|
||||
cursor:pointer;
|
||||
}
|
||||
.oauth-login-item {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<el-table-column label="标题" align="center" prop="title"/>
|
||||
|
||||
<el-table-column label="缩略图" align="center" prop="picUrl">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="缩略图片" class="img-height"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -45,17 +45,17 @@
|
||||
<el-table-column label="排序" align="center" prop="sort"/>
|
||||
<el-table-column label="描述" align="center" prop="memo"/>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['market:banner:update']">修改
|
||||
</el-button>
|
||||
@@ -107,7 +107,6 @@
|
||||
import {
|
||||
createBanner,
|
||||
deleteBanner,
|
||||
exportBannerExcel,
|
||||
getBanner,
|
||||
getBannerPage,
|
||||
updateBanner
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
« <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="品牌名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入品牌名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
@@ -26,41 +25,34 @@
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['product:brand:create']">新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['product:brand:export']">导出
|
||||
</el-button>
|
||||
v-hasPermi="['product:brand:create']">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="商品分类" align="center" prop="categoryId"/>
|
||||
<el-table-column label="品牌编号" align="center" prop="id"/>
|
||||
<el-table-column label="品牌名称" align="center" prop="name"/>
|
||||
<el-table-column label="品牌图片" align="center" prop="bannerUrl">
|
||||
<template slot-scope="scope">
|
||||
<img v-if="scope.row.bannerUrl" :src="scope.row.bannerUrl" alt="分类图片" class="img-height"/>
|
||||
<el-table-column label="品牌图片" align="center" prop="picUrl">
|
||||
<template v-slot="scope">
|
||||
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" style="height: 100px;" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="品牌排序" align="center" prop="sort"/>
|
||||
<el-table-column label="品牌描述" align="center" prop="description"/>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['product:brand:update']">修改
|
||||
</el-button>
|
||||
@@ -77,15 +69,11 @@
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="商品分类" prop="categoryId">
|
||||
<Treeselect v-model="form.categoryId" :options="categoryOptions" :normalizer="normalizer" :show-count="true"
|
||||
placeholder="请选择商品分类"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="品牌名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入品牌名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="品牌图片" prop="bannerUrl">
|
||||
<imageUpload v-model="form.bannerUrl" :limit="1"/>
|
||||
<el-form-item label="品牌图片" prop="picUrl">
|
||||
<imageUpload v-model="form.picUrl" :limit="1"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="品牌排序" prop="sort">
|
||||
<el-input v-model="form.sort" placeholder="请输入品牌排序"/>
|
||||
@@ -113,20 +101,18 @@
|
||||
import {
|
||||
createBrand,
|
||||
deleteBrand,
|
||||
exportBrandExcel,
|
||||
getBrand,
|
||||
getBrandPage,
|
||||
updateBrand
|
||||
} from "@/api/mall/product/brand";
|
||||
import {listCategory} from "@/api/mall/product/category";
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
import {CommonStatusEnum} from "@/utils/constants";
|
||||
|
||||
export default {
|
||||
name: "Brand",
|
||||
name: "ProductBrand",
|
||||
components: {
|
||||
ImageUpload, Treeselect,
|
||||
ImageUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -153,21 +139,18 @@ export default {
|
||||
status: null,
|
||||
createTime: []
|
||||
},
|
||||
// 商品分类树选项
|
||||
categoryOptions: [],
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
categoryId: [{required: true, message: "分类编号不能为空", trigger: "blur"}],
|
||||
name: [{required: true, message: "品牌名称不能为空", trigger: "blur"}],
|
||||
bannerUrl: [{required: true, message: "品牌图片不能为空", trigger: "blur"}],
|
||||
picUrl: [{required: true, message: "品牌图片不能为空", trigger: "blur"}],
|
||||
sort: [{required: true, message: "品牌排序不能为空", trigger: "blur"}],
|
||||
status: [{required: true, message: "状态不能为空", trigger: "change"}],
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getTreeselect();
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
@@ -181,26 +164,6 @@ export default {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 转换菜单数据结构 */
|
||||
normalizer(node) {
|
||||
if (node.children && !node.children.length) {
|
||||
delete node.children;
|
||||
}
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children
|
||||
};
|
||||
},
|
||||
/** 查询分类下拉树结构 */
|
||||
getTreeselect() {
|
||||
listCategory().then(response => {
|
||||
this.categoryOptions = [];
|
||||
const menu = {id: 0, name: '商品分类', children: []};
|
||||
menu.children = this.handleTree(response.data, "id", "pid");
|
||||
this.categoryOptions.push(menu);
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
@@ -210,12 +173,11 @@ export default {
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
categoryId: undefined,
|
||||
name: undefined,
|
||||
bannerUrl: undefined,
|
||||
sort: undefined,
|
||||
picUrl: undefined,
|
||||
sort: 0,
|
||||
description: undefined,
|
||||
status: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
@@ -232,14 +194,12 @@ export default {
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.getTreeselect();
|
||||
this.open = true;
|
||||
this.title = "添加品牌";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
this.getTreeselect();
|
||||
const id = row.id;
|
||||
getBrand(id).then(response => {
|
||||
this.form = response.data;
|
||||
@@ -280,30 +240,7 @@ export default {
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
params.pageNo = undefined;
|
||||
params.pageSize = undefined;
|
||||
// 执行导出
|
||||
this.$modal.confirm('是否确认导出所有品牌数据项?').then(() => {
|
||||
this.exportLoading = true;
|
||||
return exportBrandExcel(params);
|
||||
}).then(response => {
|
||||
this.$download.excel(response, "品牌.xls");
|
||||
this.exportLoading = false;
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
//
|
||||
.img-height {
|
||||
height: 150px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
<el-form-item label="分类名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入分类名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
@@ -28,42 +22,31 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['product:category:export']">导出
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id" :default-expand-all="isExpandAll"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
|
||||
<el-table-column label="分类名称" align="center" prop="name"/>
|
||||
<el-table-column label="分类图标" align="center" prop="icon">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.icon" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类图片" align="center" prop="bannerUrl">
|
||||
<template slot-scope="scope">
|
||||
<img v-if="scope.row.bannerUrl" :src="scope.row.bannerUrl" alt="分类图片" class="img-height"/>
|
||||
<el-table-column label="分类名称" prop="name"/>
|
||||
<el-table-column label="分类图片" align="center" prop="picUrl">
|
||||
<template v-slot="scope">
|
||||
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" style="height: 100px"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类排序" align="center" prop="sort"/>
|
||||
<el-table-column label="开启状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['product:category:update']">修改
|
||||
</el-button>
|
||||
@@ -78,32 +61,20 @@
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="上级分类" prop="parentId">
|
||||
<Treeselect v-model="form.parentId" :options="parentCategoryOptions" :normalizer="normalizer"
|
||||
:show-count="true"
|
||||
placeholder="上级分类"/>
|
||||
<Treeselect v-model="form.parentId" :options="parentCategoryOptions" :normalizer="normalizer" :show-count="true"
|
||||
:defaultExpandLevel="1" placeholder="上级分类"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入分类名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类图标" prop="icon">
|
||||
<el-popover placement="bottom-start" width="460" trigger="click" @show="$refs['iconSelect'].reset()">
|
||||
<IconSelect ref="iconSelect" @selected="iconSelected"/>
|
||||
<el-input slot="reference" v-model="form.icon" placeholder="点击选择分类图标" readonly>
|
||||
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon"
|
||||
style="height: 32px;width: 16px;"/>
|
||||
<i v-else slot="prefix" class="el-icon-search el-input__icon"/>
|
||||
</el-input>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类图片" prop="bannerUrl">
|
||||
<ImageUpload v-model="form.bannerUrl" :limit="1"/>
|
||||
<el-form-item label="分类图片" prop="picUrl">
|
||||
<ImageUpload v-model="form.picUrl" :limit="1" :is-show-tip="false" />
|
||||
<div v-if="form.parentId === 0" style="font-size: 10px">推荐 200x100 图片分辨率</div>
|
||||
<div v-else style="font-size: 10px">推荐 100x100 图片分辨率</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类排序" prop="sort">
|
||||
<el-input-number v-model="form.sort" controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类描述">
|
||||
<editor v-model="form.description" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
@@ -111,6 +82,9 @@
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类描述">
|
||||
<el-input v-model="form.description" type="textarea" placeholder="请输入分类描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
@@ -122,34 +96,29 @@
|
||||
|
||||
<script>
|
||||
import {
|
||||
createCategory,
|
||||
deleteCategory,
|
||||
exportCategoryExcel,
|
||||
getCategory,
|
||||
listCategory,
|
||||
updateCategory
|
||||
createProductCategory,
|
||||
deleteProductCategory,
|
||||
getProductCategory,
|
||||
getProductCategoryList,
|
||||
updateProductCategory
|
||||
} from "@/api/mall/product/category";
|
||||
import Editor from '@/components/Editor';
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
import IconSelect from "@/components/IconSelect";
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
import {CommonStatusEnum} from "@/utils/constants";
|
||||
|
||||
export default {
|
||||
name: "Category",
|
||||
name: "ProductCategory",
|
||||
components: {
|
||||
Editor, Treeselect, IconSelect, ImageUpload
|
||||
Editor, Treeselect, ImageUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 商品分类列表
|
||||
list: [],
|
||||
// 商品分类树选项
|
||||
@@ -164,10 +133,7 @@ export default {
|
||||
refreshTable: true,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
@@ -175,8 +141,8 @@ export default {
|
||||
rules: {
|
||||
parentId: [{required: true, message: "请选择上级分类", trigger: "blur"}],
|
||||
name: [{required: true, message: "分类名称不能为空", trigger: "blur"}],
|
||||
icon: [{required: true, message: "分类图标不能为空", trigger: "blur"}],
|
||||
bannerUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}],
|
||||
picUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}],
|
||||
sort: [{required: true, message: "分类排序不能为空", trigger: "blur"}],
|
||||
status: [{required: true, message: "开启状态不能为空", trigger: "blur"}],
|
||||
}
|
||||
};
|
||||
@@ -191,31 +157,12 @@ export default {
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
// 执行查询
|
||||
listCategory(params).then(response => {
|
||||
getProductCategoryList(params).then(response => {
|
||||
this.list = this.handleTree(response.data, "id", "parentId");
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 选择图标
|
||||
iconSelected(name) {
|
||||
this.form.icon = name;
|
||||
},
|
||||
/** 转换菜单数据结构 */
|
||||
normalizer(node) {
|
||||
if (node.children && !node.children.length) {
|
||||
delete node.children;
|
||||
}
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children
|
||||
};
|
||||
},
|
||||
/** 查询分类下拉树结构 */
|
||||
getTreeselect() {
|
||||
listCategory().then(response => {
|
||||
// 属性下拉框
|
||||
this.parentCategoryOptions = [];
|
||||
const menu = {id: 0, name: '主分类', children: []};
|
||||
const menu = {id: 0, name: '顶级分类', children: []};
|
||||
menu.children = this.handleTree(response.data, "id", "parentId");
|
||||
this.parentCategoryOptions.push(menu);
|
||||
});
|
||||
@@ -231,11 +178,10 @@ export default {
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
name: undefined,
|
||||
icon: undefined,
|
||||
bannerUrl: undefined,
|
||||
sort: undefined,
|
||||
pirUrl: undefined,
|
||||
sort: 0,
|
||||
description: undefined,
|
||||
status: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
@@ -257,19 +203,28 @@ export default {
|
||||
this.refreshTable = true;
|
||||
});
|
||||
},
|
||||
/** 转换菜单数据结构 */
|
||||
normalizer(node) {
|
||||
if (node.children && !node.children.length) {
|
||||
delete node.children;
|
||||
}
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children
|
||||
};
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.getTreeselect();
|
||||
this.open = true;
|
||||
this.title = "添加商品分类";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
this.getTreeselect();
|
||||
const id = row.id;
|
||||
getCategory(id).then(response => {
|
||||
getProductCategory(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改商品分类";
|
||||
@@ -283,7 +238,7 @@ export default {
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateCategory(this.form).then(response => {
|
||||
updateProductCategory(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
@@ -291,7 +246,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createCategory(this.form).then(response => {
|
||||
createProductCategory(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
@@ -302,36 +257,13 @@ export default {
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除商品分类编号为"' + id + '"的数据项?').then(function () {
|
||||
return deleteCategory(id);
|
||||
return deleteProductCategory(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
params.pageNo = undefined;
|
||||
params.pageSize = undefined;
|
||||
// 执行导出
|
||||
this.$modal.confirm('是否确认导出所有商品分类数据项?').then(() => {
|
||||
this.exportLoading = true;
|
||||
return exportCategoryExcel(params);
|
||||
}).then(response => {
|
||||
this.$download.excel(response, '商品分类.xls');
|
||||
this.exportLoading = false;
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
//
|
||||
.img-height {
|
||||
height: 150px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,14 +3,8 @@
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="规格名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入规格名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
@@ -33,17 +27,15 @@
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="规格名称" align="center" prop="name" />
|
||||
<el-table-column label="规格名称" align="center" prop="propertyValueList">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名称" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">
|
||||
<span>{{formatList(scope.row.propertyValueList)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开启状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
<router-link :to="'/property/value/' + scope.row.id" class="link-type">
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
@@ -65,36 +57,12 @@
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="规格名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入规格名称" />
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="备注" />
|
||||
</el-form-item>
|
||||
<el-form-item label="属性值">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="addPropertyValue()">添加</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-for="(domain, index) in form.propertyValueList"
|
||||
:key="domain.key"
|
||||
:prop="'propertyValueList.' + index + '.name'"
|
||||
:rules="{
|
||||
required: true, message: '属性值不能为空', trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="18">
|
||||
<el-input v-model="domain.name" size="mini"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-button style="margin-left: 20px;" size="mini" @click.prevent="removePropertyValue(domain)">删除</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
@@ -105,7 +73,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createProperty, updateProperty, deleteProperty, getProperty, getPropertyPage, exportPropertyExcel } from "@/api/mall/product/property";
|
||||
import { createProperty, updateProperty, deleteProperty, getProperty, getPropertyPage } from "@/api/mall/product/property";
|
||||
|
||||
export default {
|
||||
name: "Property",
|
||||
@@ -115,13 +83,11 @@ export default {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 规格名称列表
|
||||
// 属性项列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
@@ -132,19 +98,19 @@ export default {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
createTime: []
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
name:'',
|
||||
status:'',
|
||||
propertyValueList: [{
|
||||
name: ''
|
||||
}],
|
||||
remark:"",
|
||||
id: null,
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: "名称不能为空", trigger: "blur" }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
@@ -170,12 +136,9 @@ export default {
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
propertyValueList: [{
|
||||
name: ''
|
||||
}]
|
||||
name:'',
|
||||
remark:"",
|
||||
id: null,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
@@ -193,7 +156,7 @@ export default {
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加规格";
|
||||
this.title = "添加属性项";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
@@ -202,7 +165,7 @@ export default {
|
||||
getProperty(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改规格";
|
||||
this.title = "修改属性项";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
@@ -231,49 +194,13 @@ export default {
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除规格名称为"' + row.name + '"的数据项?').then(function() {
|
||||
this.$modal.confirm('是否确认删除名称为"' + row.name + '"的数据项?').then(function() {
|
||||
return deleteProperty(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
params.pageNo = undefined;
|
||||
params.pageSize = undefined;
|
||||
// 执行导出
|
||||
this.$modal.confirm('是否确认导出所有规格名称数据项?').then(() => {
|
||||
this.exportLoading = true;
|
||||
return exportPropertyExcel(params);
|
||||
}).then(response => {
|
||||
this.$download.excel(response, '规格名称.xls');
|
||||
this.exportLoading = false;
|
||||
}).catch(() => {});
|
||||
},
|
||||
removePropertyValue(item) {
|
||||
var index = this.form.propertyValueList.indexOf(item)
|
||||
if (index !== -1) {
|
||||
this.form.propertyValueList.splice(index, 1)
|
||||
}
|
||||
},
|
||||
addPropertyValue() {
|
||||
this.form.propertyValueList.push({
|
||||
name: ''
|
||||
});
|
||||
},
|
||||
formatList(list) {
|
||||
let str = ''
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
str += list[i].name;
|
||||
if(i != list.length-1){
|
||||
str+="/";
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
238
yudao-ui-admin/src/views/mall/product/property/value.vue
Normal file
238
yudao-ui-admin/src/views/mall/product/property/value.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="属性项" prop="propertyId">
|
||||
<el-select v-model="queryParams.propertyId">
|
||||
<el-option v-for="item in propertyOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['system:dict:create']">新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="dataList">
|
||||
<el-table-column label="编号" align="center" prop="id"/>
|
||||
<el-table-column label="名称" align="center" prop="name"/>
|
||||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dict:update']">修改
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:dict:delete']">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 添加或修改参数配置对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
|
||||
<el-form-item label="属性项">
|
||||
<el-input v-model="form.propertyId" :disabled="true"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createPropertyValue,
|
||||
deletePropertyValue,
|
||||
getProperty,
|
||||
getPropertyList,
|
||||
getPropertyValue,
|
||||
getPropertyValuePage,
|
||||
updatePropertyValue
|
||||
} from '@/api/mall/product/property'
|
||||
|
||||
export default {
|
||||
name: "PropertyValue",
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 字典表格数据
|
||||
dataList: [],
|
||||
// 默认字典类型
|
||||
defaultPropertyId: "",
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 类型数据字典
|
||||
propertyOptions: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
propertyId: undefined,
|
||||
name: undefined,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [
|
||||
{required: true, message: "名称不能为空", trigger: "blur"}
|
||||
]
|
||||
},
|
||||
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const propertyId = this.$route.params && this.$route.params.propertyId;
|
||||
this.getProperty(propertyId);
|
||||
this.getPropertyList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询字典类型详细 */
|
||||
getProperty(propertyId) {
|
||||
getProperty(propertyId).then(response => {
|
||||
this.queryParams.propertyId = response.data.id;
|
||||
this.defaultPropertyId = response.data.id;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
/** 查询字典类型列表 */
|
||||
getPropertyList() {
|
||||
getPropertyList().then(response => {
|
||||
this.propertyOptions = response.data
|
||||
});
|
||||
},
|
||||
/** 查询字典数据列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
getPropertyValuePage(this.queryParams).then(response => {
|
||||
this.dataList = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
propertyId: undefined,
|
||||
name: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.queryParams.propertyId = this.defaultPropertyId;
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加属性值";
|
||||
this.form.propertyId = this.queryParams.propertyId;
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getPropertyValue(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改属性值";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm: function () {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.id !== undefined) {
|
||||
updatePropertyValue(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
} else {
|
||||
createPropertyValue(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id;
|
||||
this.$modal.confirm('是否确认删除字典编码为"' + ids + '"的数据项?').then(function () {
|
||||
return deletePropertyValue(ids);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
const queryParams = this.queryParams;
|
||||
this.$modal.confirm('是否确认导出所有数据项?').then(() => {
|
||||
this.exportLoading = true;
|
||||
return exportData(queryParams);
|
||||
}).then(response => {
|
||||
this.$download.excel(response, '字典数据.xls');
|
||||
this.exportLoading = false;
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
580
yudao-ui-admin/src/views/mall/product/spu/save.vue
Normal file
580
yudao-ui-admin/src/views/mall/product/spu/save.vue
Normal file
@@ -0,0 +1,580 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<!-- TODO 样式优化:表单宽度、表单项对齐、hr 粗细; -->
|
||||
<el-tabs v-model="activeName" class="tabs">
|
||||
<!-- 基础设置 -->
|
||||
<!-- TODO @luowenfeng:基础设置,分成基础信息、配送信息 -->
|
||||
<el-tab-pane label="基础设置" name="basic">
|
||||
<el-form ref="basic" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
|
||||
<el-form-item label="商品名称" prop="name">
|
||||
<el-input v-model="baseForm.name" placeholder="请输入商品名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="促销语">
|
||||
<el-input type="textarea" v-model="baseForm.sellPoint" placeholder="请输入促销语"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品主图" prop="picUrls">
|
||||
<ImageUpload v-model="baseForm.picUrls" :value="baseForm.picUrls" :limit="10" class="mall-image"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品视频" prop="videoUrl">
|
||||
<VideoUpload v-model="baseForm.videoUrl" :value="baseForm.videoUrl"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品品牌" prop="brandId">
|
||||
<el-select v-model="baseForm.brandId" placeholder="请选择商品品牌">
|
||||
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品分类" prop="categoryIds">
|
||||
<el-cascader v-model="baseForm.categoryIds" placeholder="商品分类" style="width: 100%"
|
||||
:options="categoryList" :props="propName" clearable/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否上架" prop="status">
|
||||
<el-radio-group v-model="baseForm.status">
|
||||
<el-radio :label="1">立即上架</el-radio>
|
||||
<el-radio :label="0">放入仓库</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 价格库存 -->
|
||||
<!-- TODO @luowenfeng:rates=》priceStack 会更好哈 -->
|
||||
<el-tab-pane label="价格库存" name="rates" class="rates">
|
||||
<el-form ref="rates" :model="ratesForm" :rules="rules">
|
||||
<el-form-item label="启用多规格">
|
||||
<el-switch v-model="specSwitch" @change="changeSpecSwitch"/>
|
||||
</el-form-item>
|
||||
<!-- 动态添加规格属性 -->
|
||||
<div v-show="ratesForm.spec === 2">
|
||||
<div v-for="(specs, index) in dynamicSpec" :key="index" class="dynamic-spec">
|
||||
<!-- 删除按钮 -->
|
||||
<el-button type="danger" icon="el-icon-delete" circle class="spec-delete" @click="removeSpec(index)"/>
|
||||
<div class="spec-header">
|
||||
规格项:
|
||||
<el-select v-model="specs.specId" filterable placeholder="请选择" @change="changeSpec">
|
||||
<el-option v-for="item in propertyPageList" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="spec-values">
|
||||
<template v-for="(v, i) in specs.specValue">
|
||||
<el-input v-model="v.name" class="spec-value" :key="i" disabled/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" @click="dynamicSpec.push({specValue: []}); ratesForm.rates = []">添加规格项目</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 规格明细 -->
|
||||
<el-form-item label="规格明细">
|
||||
<el-table :data="ratesForm.rates" border style="width: 100%" ref="ratesTable">
|
||||
<template v-if="this.specSwitch">
|
||||
<el-table-column :key="index" v-for="(item, index) in dynamicSpec.filter(v => v.specName !== undefined)"
|
||||
:label="item.specName">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="scope.row.spec" v-model="scope.row.spec[index]" disabled/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column label="规格图片" width="120px" :render-header="addRedStar" key="90">
|
||||
<template slot-scope="scope">
|
||||
<ImageUpload v-model="scope.row.picUrl" :limit="1" :isShowTip="false" style="width: 100px; height: 50px"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="市场价(元)" :render-header="addRedStar" key="92">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item :prop="'rates.'+ scope.$index + '.marketPrice'" :rules="[{required: true, trigger: 'change'}]">
|
||||
<el-input v-model="scope.row.marketPrice"
|
||||
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="销售价(元)" :render-header="addRedStar" key="93">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item :prop="'rates.'+ scope.$index + '.price'"
|
||||
:rules="[{required: true, trigger: 'change'}]">
|
||||
<el-input v-model="scope.row.price"
|
||||
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="成本价" :render-header="addRedStar" key="94">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item :prop="'rates.'+ scope.$index + '.costPrice'"
|
||||
:rules="[{required: true, trigger: 'change'}]">
|
||||
<el-input v-model="scope.row.costPrice"
|
||||
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库存" :render-header="addRedStar" key="95">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item :prop="'rates.'+ scope.$index + '.stock'" :rules="[{required: true, trigger: 'change'}]">
|
||||
<el-input v-model="scope.row.stock" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预警库存" key="96">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.warnStock" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="体积" key="97">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.volume" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重量" key="98">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.weight" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="条码" key="99">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.barCode" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-if="this.specSwitch">
|
||||
<el-table-column fixed="right" label="操作" width="50" key="100">
|
||||
<template slot-scope="scope">
|
||||
<el-button @click="scope.row.status = 1" type="text" size="small"
|
||||
v-show="scope.row.status === undefined || scope.row.status === 0 ">禁用
|
||||
</el-button>
|
||||
<el-button @click="scope.row.status = 0" type="text" size="small" v-show="scope.row.status === 1">
|
||||
启用
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
<el-form-item label="虚拟销量" prop="virtualSalesCount">
|
||||
<el-input v-model="baseForm.virtualSalesCount" placeholder="请输入虚拟销量"
|
||||
oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 商品详情 -->
|
||||
<el-tab-pane label="商品详情" name="detail">
|
||||
<el-form ref="detail" :model="baseForm" :rules="rules">
|
||||
<el-form-item prop="description">
|
||||
<editor v-model="baseForm.description" :min-height="380"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 销售设置 -->
|
||||
<el-tab-pane label="高级设置" name="senior">
|
||||
<el-form ref="senior" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
|
||||
<el-form-item label="排序字段">
|
||||
<el-input v-model="baseForm.sort" placeholder="请输入排序字段" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否展示库存" prop="showStock">
|
||||
<el-radio-group v-model="baseForm.showStock">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="buttons">
|
||||
<el-button type="info" round @click="cancel">取消</el-button>
|
||||
<el-button type="success" round @click="submit">确认</el-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {getBrandList} from "@/api/mall/product/brand";
|
||||
import {getProductCategoryList} from "@/api/mall/product/category";
|
||||
import {createSpu, getSpuDetail, updateSpu} from "@/api/mall/product/spu";
|
||||
import {getPropertyListAndValue,} from "@/api/mall/product/property";
|
||||
import Editor from "@/components/Editor";
|
||||
import ImageUpload from "@/components/ImageUpload";
|
||||
import VideoUpload from "@/components/VideoUpload";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Editor,
|
||||
ImageUpload,
|
||||
VideoUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
specSwitch: false,
|
||||
activeName: "basic",
|
||||
propName: {
|
||||
checkStrictly: true,
|
||||
label: "name",
|
||||
value: "id",
|
||||
},
|
||||
// 基础设置
|
||||
baseForm: {
|
||||
id: null,
|
||||
name: null,
|
||||
sellPoint: null,
|
||||
categoryIds: null,
|
||||
sort: null,
|
||||
description: null,
|
||||
picUrls: null,
|
||||
videoUrl: null,
|
||||
status: 0,
|
||||
virtualSalesCount: 0,
|
||||
showStock: true,
|
||||
brandId: null,
|
||||
},
|
||||
|
||||
categoryList: [],
|
||||
// 价格库存
|
||||
ratesForm: {
|
||||
spec: 1,
|
||||
// 规格明细
|
||||
rates: [{}]
|
||||
},
|
||||
dynamicSpec: [
|
||||
// {
|
||||
// specId: 86,
|
||||
// specName: "颜色",
|
||||
// specValue:[{
|
||||
// name: "红色",
|
||||
// id: 225,
|
||||
// }]
|
||||
// },
|
||||
],
|
||||
propertyPageList: [],
|
||||
brandList: [],
|
||||
specValue: null,
|
||||
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{required: true, message: "商品名称不能为空", trigger: "blur"},],
|
||||
description: [{required: true, message: "描述不能为空", trigger: "blur"},],
|
||||
categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"},],
|
||||
status: [{required: true, message: "商品状态不能为空", trigger: "blur"}],
|
||||
brandId: [{required: true, message: "商品品牌不能为空", trigger: "blur"}],
|
||||
picUrls: [{required: true, message: "商品轮播图地址不能为空", trigger: "blur"}],
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getListBrand();
|
||||
this.getListCategory();
|
||||
this.getPropertyPageList();
|
||||
const spuId = this.$route.params && this.$route.params.spuId;
|
||||
if (spuId != null) {
|
||||
this.updateType(spuId)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
removeSpec(index) {
|
||||
this.dynamicSpec.splice(index, 1);
|
||||
this.changeSpecSwitch()
|
||||
},
|
||||
// 必选标识
|
||||
addRedStar(h, {column}) {
|
||||
return [
|
||||
h('span', {style: 'color: #F56C6C'}, '*'),
|
||||
h('span', ' ' + column.label)
|
||||
];
|
||||
},
|
||||
changeSpecSwitch() {
|
||||
this.specSwitch ? this.ratesForm.spec = 2 : this.ratesForm.spec = 1;
|
||||
this.$refs.ratesTable.doLayout();
|
||||
if (this.ratesForm.spec === 1) {
|
||||
this.ratesForm.rates = [{}]
|
||||
} else {
|
||||
this.ratesForm.rates = []
|
||||
if (this.dynamicSpec.length > 0) {
|
||||
this.buildRatesFormRates()
|
||||
}
|
||||
}
|
||||
},
|
||||
// 构建规格明细笛卡尔积
|
||||
buildRatesFormRates() {
|
||||
let rates = [];
|
||||
this.dynamicSpec.map(v => v.specValue.map(m => m.name))
|
||||
.reduce((last, current) => {
|
||||
const array = [];
|
||||
last.forEach(par1 => {
|
||||
current.forEach(par2 => {
|
||||
let v
|
||||
// 当两个对象合并时,需使用[1,2]方式生成数组,而当数组和对象合并时,需使用concat
|
||||
if (par1 instanceof Array) {
|
||||
v = par1.concat(par2)
|
||||
} else {
|
||||
v = [par1, par2];
|
||||
}
|
||||
array.push(v)
|
||||
});
|
||||
});
|
||||
return array;
|
||||
})
|
||||
.forEach(v => {
|
||||
let spec = v;
|
||||
// 当v为单个规格项时,会变成字符串。造成表格只截取第一个字符串,而不是数组的第一个元素
|
||||
if (typeof v == 'string') {
|
||||
spec = Array.of(v)
|
||||
}
|
||||
rates.push({spec: spec, status: 0, name: Array.of(v).join()})
|
||||
});
|
||||
this.ratesForm.rates = rates
|
||||
},
|
||||
/** 查询分类 */
|
||||
getListCategory() {
|
||||
// 执行查询
|
||||
getProductCategoryList().then((response) => {
|
||||
this.categoryList = this.handleTree(response.data, "id", "parentId");
|
||||
});
|
||||
},
|
||||
/** 查询品牌列表 */
|
||||
getListBrand() {
|
||||
// 执行查询
|
||||
getBrandList().then((response) => {
|
||||
this.brandList = response.data;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
var currentView = this.$store.state.tagsView.visitedViews[0]
|
||||
for (currentView of this.$store.state.tagsView.visitedViews) {
|
||||
if (currentView.path === this.$route.path) {
|
||||
break
|
||||
}
|
||||
}
|
||||
this.$store.dispatch('tagsView/delView', currentView)
|
||||
.then(() => {
|
||||
this.$router.push("/product/spu")
|
||||
})
|
||||
},
|
||||
submit() {
|
||||
this.$refs[this.activeName].validate((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
let rates = JSON.parse(JSON.stringify(this.ratesForm.rates));
|
||||
|
||||
// 价格元转分
|
||||
rates.forEach(r => {
|
||||
r.marketPrice = r.marketPrice * 100;
|
||||
r.price = r.price * 100;
|
||||
r.costPrice = r.costPrice * 100;
|
||||
})
|
||||
|
||||
// 动态规格调整字段
|
||||
if (this.specSwitch) {
|
||||
rates.forEach(r => {
|
||||
let properties = []
|
||||
Array.of(r.spec).forEach(s => {
|
||||
let obj;
|
||||
if (s instanceof Array) {
|
||||
obj = s;
|
||||
} else {
|
||||
obj = Array.of(s);
|
||||
}
|
||||
obj.forEach((v, i) => {
|
||||
let specValue = this.dynamicSpec[i].specValue.find(o => o.name === v);
|
||||
let propertie = {};
|
||||
propertie.propertyId = this.dynamicSpec[i].specId;
|
||||
propertie.valueId = specValue.id;
|
||||
properties.push(propertie);
|
||||
})
|
||||
})
|
||||
r.properties = properties;
|
||||
})
|
||||
} else {
|
||||
rates[0].name = this.baseForm.name;
|
||||
rates[0].status = this.baseForm.status;
|
||||
}
|
||||
let form = this.baseForm
|
||||
if (form.picUrls instanceof Array) {
|
||||
form.picUrls = form.picUrls.flatMap(m => m.split(','))
|
||||
} else if (form.picUrls.split(',') instanceof Array) {
|
||||
form.picUrls = form.picUrls.split(',').flatMap(m => m.split(','))
|
||||
} else {
|
||||
form.picUrls = Array.of(form.picUrls)
|
||||
}
|
||||
form.skus = rates;
|
||||
form.specType = this.ratesForm.spec;
|
||||
|
||||
let category = form.categoryIds instanceof Array ? form.categoryIds: Array.of(form.categoryIds)
|
||||
console.log(category)
|
||||
form.categoryId = category[category.length - 1];
|
||||
|
||||
if (form.id == null) {
|
||||
createSpu(form).then(() => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
}).then(()=>{
|
||||
this.cancel();
|
||||
})
|
||||
} else {
|
||||
updateSpu(form).then(() => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
}).then(()=>{
|
||||
this.cancel();
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
/** 查询规格 */
|
||||
getPropertyPageList() {
|
||||
// 执行查询
|
||||
getPropertyListAndValue().then((response) => {
|
||||
this.propertyPageList = response.data;
|
||||
});
|
||||
},
|
||||
// 添加规格项目
|
||||
changeSpec(val) {
|
||||
let obj = this.propertyPageList.find(o => o.id === val);
|
||||
let spec = this.dynamicSpec.find(o => o.specId === val)
|
||||
spec.specId = obj.id;
|
||||
spec.specName = obj.name;
|
||||
spec.specValue = obj.values;
|
||||
this.buildRatesFormRates();
|
||||
},
|
||||
updateType(id) {
|
||||
getSpuDetail(id).then((response) => {
|
||||
let data = response.data;
|
||||
this.baseForm.id = data.id;
|
||||
this.baseForm.name = data.name;
|
||||
this.baseForm.sellPoint = data.sellPoint;
|
||||
this.baseForm.categoryIds = data.categoryId;
|
||||
this.baseForm.videoUrl = data.videoUrl;
|
||||
this.baseForm.sort = data.sort;
|
||||
this.baseForm.description = data.description;
|
||||
this.baseForm.picUrls = data.picUrls;
|
||||
this.baseForm.status = data.status;
|
||||
this.baseForm.virtualSalesCount = data.virtualSalesCount;
|
||||
this.baseForm.showStock = data.showStock;
|
||||
this.baseForm.brandId = data.brandId;
|
||||
this.ratesForm.spec = data.specType;
|
||||
data.skus.forEach(r => {
|
||||
r.marketPrice = this.divide(r.marketPrice, 100)
|
||||
r.price = this.divide(r.price, 100)
|
||||
r.costPrice = this.divide(r.costPrice, 100)
|
||||
})
|
||||
if (this.ratesForm.spec === 2) {
|
||||
this.specSwitch = true;
|
||||
data.productPropertyViews.forEach(p => {
|
||||
let obj = {};
|
||||
obj.specId = p.propertyId;
|
||||
obj.specName = p.name;
|
||||
obj.specValue = p.propertyValues;
|
||||
this.dynamicSpec.push(obj);
|
||||
})
|
||||
data.skus.forEach(s => {
|
||||
s.spec = [];
|
||||
s.properties.forEach(sp => {
|
||||
let spec = data.productPropertyViews.find(o => o.propertyId === sp.propertyId).propertyValues.find(v => v.id === sp.valueId).name;
|
||||
s.spec.push(spec)
|
||||
})
|
||||
})
|
||||
}
|
||||
this.ratesForm.rates = data.skus
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container{
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dynamic-spec {
|
||||
background-color: #f2f2f2;
|
||||
width: 85%;
|
||||
margin: auto;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.spec-header {
|
||||
padding: 30px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.spec-name {
|
||||
display: inline;
|
||||
|
||||
input {
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spec-values {
|
||||
width: 84%;
|
||||
padding: 25px;
|
||||
margin: auto;
|
||||
padding-top: 5px;
|
||||
|
||||
.spec-value {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
width: 13%;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.spec-delete {
|
||||
float: right;
|
||||
margin-top: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
border-bottom: 2px solid #f2f2f2;
|
||||
|
||||
.el-tab-pane {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 库存价格图片样式修改
|
||||
.rates {
|
||||
.component-upload-image {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.el-upload--picture-card {
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
line-height: 60px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.el-upload-list__item {
|
||||
width: 100px !important;
|
||||
height: 50px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 20px;
|
||||
height: 36px;
|
||||
|
||||
button {
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.mall-image {
|
||||
.el-upload--picture-card {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
line-height: 90px;
|
||||
}
|
||||
|
||||
.el-upload-list__item {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
162
yudao-ui-admin/src/views/mall/promotion/coupon/index.vue
Executable file
162
yudao-ui-admin/src/views/mall/promotion/coupon/index.vue
Executable file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="会员昵称" prop="nickname">
|
||||
<el-input v-model="queryParams.nickname" placeholder="请输入会员昵称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- Tab 选项:真正的内容在 Lab -->
|
||||
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
|
||||
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
|
||||
</el-tabs>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="会员信息" align="center" prop="nickname" /> <!-- TODO 芋艿:以后支持头像,支持跳转 -->
|
||||
<el-table-column label="优惠劵" align="center" prop="name" />
|
||||
<el-table-column label="优惠券类型" align="center" prop="discountType">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="领取方式" align="center" prop="takeType">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE" :value="scope.row.takeType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="领取时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="使用时间" align="center" prop="useTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.useTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['promotion:coupon:delete']">回收</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { deleteCoupon, getCouponPage } from "@/api/mall/promotion/coupon";
|
||||
import { DICT_TYPE, getDictDatas} from "@/utils/dict";
|
||||
|
||||
export default {
|
||||
name: "Coupon",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 优惠劵列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
createTime: [],
|
||||
status: undefined,
|
||||
},
|
||||
// Tab 筛选
|
||||
activeTab: 'all',
|
||||
statusTabs: [{
|
||||
label: '全部',
|
||||
value: 'all'
|
||||
}],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
// 设置 statuses 过滤
|
||||
for (const dict of getDictDatas(DICT_TYPE.PROMOTION_COUPON_STATUS)) {
|
||||
this.statusTabs.push({
|
||||
label: dict.label,
|
||||
value: dict.value
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getCouponPage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('回收将会收回会员领取的待使用的优惠券,已使用的将无法回收,确定要回收所选优惠券吗?').then(function() {
|
||||
return deleteCoupon(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("回收成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** tab 切换 */
|
||||
tabClick(tab) {
|
||||
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
|
||||
this.getList();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
403
yudao-ui-admin/src/views/mall/promotion/couponTemplate/index.vue
Executable file
403
yudao-ui-admin/src/views/mall/promotion/couponTemplate/index.vue
Executable file
@@ -0,0 +1,403 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="82px">
|
||||
<el-form-item label="优惠券名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入优惠劵名" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="优惠券类型" prop="discountType">
|
||||
<el-select v-model="queryParams.discountType" placeholder="请选择优惠券类型" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="优惠券状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择优惠券状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['promotion:coupon-template:create']">新增</el-button>
|
||||
<el-button type="info" plain icon="el-icon-s-operation" size="mini"
|
||||
@click="() => this.$router.push('/promotion/coupon')"
|
||||
v-hasPermi="['promotion:coupon:query']">会员优惠劵</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="优惠券名称" align="center" prop="name" />
|
||||
<el-table-column label="优惠券类型" align="center" prop="discountType">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="优惠金额 / 折扣" align="center" prop="discount" :formatter="discountFormat" />
|
||||
<el-table-column label="发放数量" align="center" prop="totalCount" />
|
||||
<el-table-column label="剩余数量" align="center" prop="totalCount" :formatter="row => (row.totalCount - row.takeCount)" />
|
||||
<el-table-column label="领取上限" align="center" prop="takeLimitCount" :formatter="takeLimitCountFormat" />
|
||||
<el-table-column label="有效期限" align="center" prop="validityType" width="180" :formatter="validityTypeFormat" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['promotion:coupon-template:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['promotion:coupon-template:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
|
||||
<el-form-item label="优惠券名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入优惠券名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="优惠券类型" prop="discountType">
|
||||
<el-radio-group v-model="form.discountType">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PRICE.type" label="优惠券面额" prop="discountPrice">
|
||||
<el-input-number v-model="form.discountPrice" placeholder="请输入优惠金额,单位:元"
|
||||
style="width: 400px" :precision="2" :min="0" /> 元
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PERCENT.type" label="优惠券折扣" prop="discountPercent">
|
||||
<el-input-number v-model="form.discountPercent" placeholder="优惠券折扣不能小于 1 折,且不可大于 9.9 折"
|
||||
style="width: 400px" :precision="1" :min="1" :max="9.9" /> 折
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PERCENT.type" label="最多优惠" prop="discountLimitPrice">
|
||||
<el-input-number v-model="form.discountLimitPrice" placeholder="请输入最多优惠"
|
||||
style="width: 400px" :precision="2" :min="0" /> 元
|
||||
</el-form-item>
|
||||
<el-form-item label="满多少元可以使用" prop="usePrice">
|
||||
<el-input-number v-model="form.usePrice" placeholder="无门槛请设为 0"
|
||||
style="width: 400px" :precision="2" :min="0" /> 元
|
||||
</el-form-item>
|
||||
<el-form-item label="领取方式" prop="takeType">
|
||||
<el-radio-group v-model="form.takeType">
|
||||
<el-radio :key="1" :label="1">直接领取</el-radio>
|
||||
<el-radio :key="2" :label="2">指定发放</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.takeType === 1" label="发放数量" prop="totalCount">
|
||||
<el-input-number v-model="form.totalCount" placeholder="发放数量,没有之后不能领取或发放,-1 为不限制"
|
||||
style="width: 400px" :precision="0" :min="-1" /> 张
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.takeType === 1" label="每人限领个数" prop="takeLimitCount">
|
||||
<el-input-number v-model="form.takeLimitCount" placeholder="设置为 -1 时,可无限领取"
|
||||
style="width: 400px" :precision="0" :min="-1" /> 张
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期类型" prop="validityType">
|
||||
<el-radio-group v-model="form.validityType">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.DATE.type" label="固定日期" prop="validTimes">
|
||||
<el-date-picker v-model="form.validTimes" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
|
||||
:default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.TERM.type" label="领取日期" prop="fixedStartTerm">
|
||||
第 <el-input-number v-model="form.fixedStartTerm" placeholder="0 为今天生效"
|
||||
style="width: 165px" :precision="0" :min="0"/> 至
|
||||
<el-input-number v-model="form.fixedEndTerm" placeholder="请输入结束天数"
|
||||
style="width: 165px" :precision="0" :min="0"/> 天有效
|
||||
</el-form-item>
|
||||
<el-form-item label="活动商品" prop="productScope">
|
||||
<el-radio-group v-model="form.productScope">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
|
||||
<el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
|
||||
multiple filterable style="width: 400px">
|
||||
<el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.name }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.minPrice / 100.0).toFixed(2) }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createCouponTemplate,
|
||||
updateCouponTemplate,
|
||||
deleteCouponTemplate,
|
||||
getCouponTemplate,
|
||||
getCouponTemplatePage,
|
||||
updateCouponTemplateStatus
|
||||
} from "@/api/mall/promotion/couponTemplate";
|
||||
import {
|
||||
CommonStatusEnum,
|
||||
CouponTemplateValidityTypeEnum,
|
||||
PromotionDiscountTypeEnum,
|
||||
PromotionProductScopeEnum
|
||||
} from "@/utils/constants";
|
||||
import { getSpuSimpleList } from "@/api/mall/product/spu";
|
||||
import { parseTime } from "@/utils/ruoyi";
|
||||
import {changeRoleStatus} from "@/api/system/role";
|
||||
|
||||
export default {
|
||||
name: "CouponTemplate",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 优惠劵列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
type: null,
|
||||
createTime: [],
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{ required: true, message: "优惠券名称不能为空", trigger: "blur" }],
|
||||
discountType: [{ required: true, message: "优惠券类型不能为空", trigger: "change" }],
|
||||
discountPrice: [{ required: true, message: "优惠券面额不能为空", trigger: "blur" }],
|
||||
discountPercent: [{ required: true, message: "优惠券折扣不能为空", trigger: "blur" }],
|
||||
discountLimitPrice: [{ required: true, message: "最多优惠不能为空", trigger: "blur" }],
|
||||
usePrice: [{ required: true, message: "满多少元可以使用不能为空", trigger: "blur" }],
|
||||
takeType: [{ required: true, message: "领取方式不能为空", trigger: "change" }],
|
||||
totalCount: [{ required: true, message: "发放数量不能为空", trigger: "blur" }],
|
||||
takeLimitCount: [{ required: true, message: "每人限领个数不能为空", trigger: "blur" }],
|
||||
validityType: [{ required: true, message: "有效期类型不能为空", trigger: "change" }],
|
||||
validTimes: [{ required: true, message: "固定日期不能为空", trigger: "change" }],
|
||||
fixedStartTerm: [{ required: true, message: "开始领取天数不能为空", trigger: "blur" }],
|
||||
fixedEndTerm: [{ required: true, message: "开始领取天数不能为空", trigger: "blur" }],
|
||||
productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
|
||||
productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
|
||||
},
|
||||
// 商品列表
|
||||
productSpus: [],
|
||||
// 如下的变量,主要为了 v-if 判断可以使用到
|
||||
PromotionProductScopeEnum: PromotionProductScopeEnum,
|
||||
CouponTemplateValidityTypeEnum: CouponTemplateValidityTypeEnum,
|
||||
PromotionDiscountTypeEnum: PromotionDiscountTypeEnum,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getCouponTemplatePage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
// 查询商品列表
|
||||
getSpuSimpleList().then(response => {
|
||||
this.productSpus = response.data
|
||||
})
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
discountType: PromotionDiscountTypeEnum.PRICE.type,
|
||||
discountPrice: undefined,
|
||||
discountPercent: undefined,
|
||||
discountLimitPrice: undefined,
|
||||
usePrice: undefined,
|
||||
takeType: 1,
|
||||
totalCount: undefined,
|
||||
takeLimitCount: undefined,
|
||||
validityType: CouponTemplateValidityTypeEnum.DATE.type,
|
||||
validTimes: [],
|
||||
validStartTime: undefined,
|
||||
validEndTime: undefined,
|
||||
fixedStartTerm: undefined,
|
||||
fixedEndTerm: undefined,
|
||||
productScope: PromotionProductScopeEnum.ALL.scope,
|
||||
productSpuIds: [],
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加优惠劵";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getCouponTemplate(id).then(response => {
|
||||
this.form = {
|
||||
...response.data,
|
||||
discountPrice: response.data.discountPrice !== undefined ? response.data.discountPrice / 100.0 : undefined,
|
||||
discountPercent: response.data.discountPercent !== undefined ? response.data.discountPercent / 10.0 : undefined,
|
||||
discountLimitPrice: response.data.discountLimitPrice !== undefined ? response.data.discountLimitPrice / 100.0 : undefined,
|
||||
usePrice: response.data.usePrice !== undefined ? response.data.usePrice / 100.0 : undefined,
|
||||
validTimes: [response.data.validStartTime, response.data.validEndTime]
|
||||
}
|
||||
this.open = true;
|
||||
this.title = "修改优惠劵";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 金额相关字段的缩放
|
||||
let data = {
|
||||
...this.form,
|
||||
discountPrice: this.form.discountPrice !== undefined ? this.form.discountPrice * 100 : undefined,
|
||||
discountPercent: this.form.discountPercent !== undefined ? this.form.discountPercent * 10 : undefined,
|
||||
discountLimitPrice: this.form.discountLimitPrice !== undefined ? this.form.discountLimitPrice * 100 : undefined,
|
||||
usePrice: this.form.usePrice !== undefined ? this.form.usePrice * 100 : undefined,
|
||||
validStartTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[0] : undefined,
|
||||
validEndTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[1] : undefined,
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateCouponTemplate(data).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createCouponTemplate(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 优惠劵模板状态修改 */
|
||||
handleStatusChange(row) {
|
||||
// 此时,row 已经变成目标状态了,所以可以直接提交请求和提示
|
||||
let text = row.status === CommonStatusEnum.ENABLE ? "启用" : "停用";
|
||||
this.$modal.confirm('确认要"' + text + '""' + row.name + '"优惠劵吗?').then(function() {
|
||||
return updateCouponTemplateStatus(row.id, row.status);
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess(text + "成功");
|
||||
}).catch(function() {
|
||||
// 异常时,需要将 row.status 状态重置回之前的
|
||||
row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE
|
||||
: CommonStatusEnum.ENABLE;
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除优惠劵编号为"' + id + '"的数据项?').then(function() {
|
||||
return deleteCouponTemplate(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
// 格式化【优惠金额/折扣】
|
||||
discountFormat(row, column) {
|
||||
if (row.discountType === PromotionDiscountTypeEnum.PRICE.type) {
|
||||
return `¥${(row.discountPrice / 100.0).toFixed(2)}`;
|
||||
}
|
||||
if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
|
||||
return `¥${(row.discountPrice / 100.0).toFixed(2)}`;
|
||||
}
|
||||
return '未知【' + row.discountType + '】';
|
||||
},
|
||||
// 格式化【领取上限】
|
||||
takeLimitCountFormat(row, column) {
|
||||
if (row.takeLimitCount === -1) {
|
||||
return '无领取限制';
|
||||
}
|
||||
return `${row.takeLimitCount} 张/人`
|
||||
},
|
||||
// 格式化【有效期限】
|
||||
validityTypeFormat(row, column) {
|
||||
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
|
||||
return `${parseTime(row.validStartTime)} 至 ${parseTime(row.validEndTime)}`
|
||||
}
|
||||
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
|
||||
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`
|
||||
}
|
||||
return '未知【' + row.validityType + '】';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
382
yudao-ui-admin/src/views/mall/promotion/discountActivity/index.vue
Executable file
382
yudao-ui-admin/src/views/mall/promotion/discountActivity/index.vue
Executable file
@@ -0,0 +1,382 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['promotion:discount-activity:create']">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="活动名称" align="center" prop="name" />
|
||||
<el-table-column label="活动时间" align="center" prop="startTime" width="240">
|
||||
<template slot-scope="scope">
|
||||
<div>开始:{{ parseTime(scope.row.startTime) }}</div>
|
||||
<div>结束:{{ parseTime(scope.row.endTime) }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
|
||||
v-hasPermi="['promotion:discount-activity:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
|
||||
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
|
||||
scope.row.status !== PromotionActivityStatusEnum.END.type"
|
||||
v-hasPermi="['promotion:discount-activity:close']">关闭</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
|
||||
v-hasPermi="['promotion:discount-activity:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="1000px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入活动名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动时间" prop="startAndEndTime">
|
||||
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
|
||||
value-format="timestamp" placeholder="选择开始时间" style="width: 880px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品选择">
|
||||
<el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small"
|
||||
multiple filterable style="width: 880px" @change="changeFormSku">
|
||||
<el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.spuName }} {{ item.name}}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.price / 100.0).toFixed(2) }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-table v-loading="loading" :data="form.products">
|
||||
<el-table-column label="商品名称" align="center" width="200">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.spuName }} {{ scope.row.name}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品价格" align="center" prop="price">
|
||||
<template slot-scope="scope">
|
||||
¥{{ (scope.row.price / 100.0).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库存" align="center" prop="stock" />
|
||||
<el-table-column label="优惠类型" align="center" property="discountType">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.discountType" placeholder="请选择优惠类型">
|
||||
<el-option v-for="dict in getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="优惠" align="center" prop="startTime" width="250">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PRICE.type" prop="discountPrice">
|
||||
减 <el-input-number v-model="scope.row.discountPrice" placeholder="请输入优惠金额"
|
||||
style="width: 190px" :precision="2" :min="0" :max="scope.row.price / 100.0 - 0.01" /> 元
|
||||
</el-form-item>
|
||||
<el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PERCENT.type" prop="discountPercent">
|
||||
打 <el-input-number v-model="scope.row.discountPercent" placeholder="请输入优惠折扣"
|
||||
style="width: 190px" :precision="1" :min="1" :max="9.9" /> 折
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="removeFormSku(scope.row.skuId)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createDiscountActivity,
|
||||
updateDiscountActivity,
|
||||
deleteDiscountActivity,
|
||||
getDiscountActivity,
|
||||
getDiscountActivityPage,
|
||||
closeDiscountActivity
|
||||
} from "@/api/mall/promotion/discountActivity";
|
||||
import {
|
||||
PromotionActivityStatusEnum, PromotionDiscountTypeEnum,
|
||||
PromotionProductScopeEnum
|
||||
} from "@/utils/constants";
|
||||
import { getSkuOptionList } from "@/api/mall/product/sku";
|
||||
import { deepClone } from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "DiscountActivity",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 限时折扣活动列表
|
||||
list: [],
|
||||
// 弹出层名称
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
createTime: [],
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
skuIds: [], // 选中的 SKU
|
||||
products: [], // 商品信息
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
|
||||
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
|
||||
skuIds: [{ required: true, message: "选择商品不能为空", trigger: "blur" }],
|
||||
},
|
||||
// 商品 SKU 列表
|
||||
productSkus: [],
|
||||
// 如下的变量,主要为了 v-if 判断可以使用到
|
||||
PromotionProductScopeEnum: PromotionProductScopeEnum,
|
||||
PromotionActivityStatusEnum: PromotionActivityStatusEnum,
|
||||
PromotionDiscountTypeEnum: PromotionDiscountTypeEnum,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getDiscountActivityPage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
// 获得 SKU 商品列表
|
||||
getSkuOptionList().then(response => {
|
||||
this.productSkus = response.data;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
startAndEndTime: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
remark: undefined,
|
||||
skuIds: [],
|
||||
products: [],
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加限时折扣活动";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getDiscountActivity(id).then(response => {
|
||||
this.form = response.data;
|
||||
// 修改数据
|
||||
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
|
||||
this.form.skuIds = response.data.products.map(item => item.skuId);
|
||||
this.form.products.forEach(product => {
|
||||
// 获得对应的 SKU 信息
|
||||
const sku = this.productSkus.find(item => item.id === product.skuId);
|
||||
if (!sku) {
|
||||
return;
|
||||
}
|
||||
// 设置商品信息
|
||||
product.name = sku.name;
|
||||
product.spuName = sku.spuName;
|
||||
product.price = sku.price;
|
||||
product.stock = sku.stock;
|
||||
product.discountPrice = product.discountPrice !== undefined ? product.discountPrice / 100.0 : undefined;
|
||||
product.discountPercent = product.discountPercent !== undefined ? product.discountPercent / 10.0 : undefined;
|
||||
});
|
||||
// 打开弹窗
|
||||
this.open = true;
|
||||
this.title = "修改限时折扣活动";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 处理数据
|
||||
const data = deepClone(this.form); // 必须深拷贝,不然后面的 products 操作会有影响
|
||||
data.startTime = this.form.startAndEndTime[0];
|
||||
data.endTime = this.form.startAndEndTime[1];
|
||||
data.products.forEach(product => {
|
||||
product.discountPrice = product.discountPrice !== undefined ? product.discountPrice * 100 : undefined;
|
||||
product.discountPercent = product.discountPercent !== undefined ? product.discountPercent * 10 : undefined;
|
||||
});
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateDiscountActivity(data).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createDiscountActivity(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除限时折扣活动编号为"' + id + '"的数据项?').then(function() {
|
||||
return deleteDiscountActivity(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 关闭按钮操作 */
|
||||
handleClose(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认关闭限时折扣活动编号为"' + id + '"的数据项?').then(function() {
|
||||
return closeDiscountActivity(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("关闭成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 当 Form 的 SKU 发生变化时 */
|
||||
changeFormSku(skuIds) {
|
||||
// 处理【新增】
|
||||
skuIds.forEach(skuId => {
|
||||
// 获得对应的 SKU 信息
|
||||
const sku = this.productSkus.find(item => item.id === skuId);
|
||||
if (!sku) {
|
||||
return;
|
||||
}
|
||||
// 判断已存在,直接跳过
|
||||
const product = this.form.products.find(item => item.skuId === skuId);
|
||||
if (product) {
|
||||
return;
|
||||
}
|
||||
this.form.products.push({
|
||||
skuId: sku.id,
|
||||
name: sku.name,
|
||||
price: sku.price,
|
||||
stock: sku.stock,
|
||||
spuId: sku.spuId,
|
||||
spuName: sku.spuName,
|
||||
discountType: PromotionDiscountTypeEnum.PRICE.type,
|
||||
});
|
||||
});
|
||||
// 处理【移除】
|
||||
this.form.products.map((product, index) => {
|
||||
if (!skuIds.includes(product.skuId)) {
|
||||
this.form.products.splice(index, 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 移除 Form 的 SKU */
|
||||
removeFormSku(skuId) {
|
||||
this.form.skuIds.map((id, index) => {
|
||||
if (skuId === id) {
|
||||
this.form.skuIds.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.changeFormSku(this.form.skuIds);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
305
yudao-ui-admin/src/views/mall/promotion/rewardActivity/index.vue
Executable file
305
yudao-ui-admin/src/views/mall/promotion/rewardActivity/index.vue
Executable file
@@ -0,0 +1,305 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['promotion:reward-activity:create']">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="活动名称" align="center" prop="name" />
|
||||
<el-table-column label="活动时间" align="center" prop="startTime" width="240">
|
||||
<template slot-scope="scope">
|
||||
<div>开始:{{ parseTime(scope.row.startTime) }}</div>
|
||||
<div>结束:{{ parseTime(scope.row.endTime) }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
|
||||
v-hasPermi="['promotion:reward-activity:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
|
||||
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
|
||||
scope.row.status !== PromotionActivityStatusEnum.END.type"
|
||||
v-hasPermi="['promotion:reward-activity:close']">关闭</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
|
||||
v-hasPermi="['promotion:reward-activity:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入活动名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动时间" prop="startAndEndTime">
|
||||
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
|
||||
value-format="timestamp" placeholder="选择开始时间" style="width: 480px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="条件类型" prop="conditionType">
|
||||
<el-radio-group v-model="form.conditionType">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_CONDITION_TYPE)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="优惠设置" prop="conditionType">
|
||||
<!-- TODO 芋艿:待实现! -->
|
||||
</el-form-item>
|
||||
<el-form-item label="活动商品" prop="productScope">
|
||||
<el-radio-group v-model="form.productScope">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
|
||||
<el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
|
||||
multiple filterable style="width: 400px">
|
||||
<el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.name }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.minPrice / 100.0).toFixed(2) }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createRewardActivity,
|
||||
updateRewardActivity,
|
||||
deleteRewardActivity,
|
||||
getRewardActivity,
|
||||
getRewardActivityPage,
|
||||
closeRewardActivity
|
||||
} from "@/api/mall/promotion/rewardActivity";
|
||||
import {
|
||||
PromotionConditionTypeEnum,
|
||||
PromotionProductScopeEnum,
|
||||
PromotionActivityStatusEnum
|
||||
} from "@/utils/constants";
|
||||
import {getSpuSimpleList} from "@/api/mall/product/spu";
|
||||
|
||||
export default {
|
||||
name: "RewardActivity",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 满减送活动列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
|
||||
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
|
||||
conditionType: [{ required: true, message: "条件类型不能为空", trigger: "change" }],
|
||||
productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
|
||||
productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
|
||||
},
|
||||
// 商品列表
|
||||
productSpus: [],
|
||||
// 如下的变量,主要为了 v-if 判断可以使用到
|
||||
PromotionProductScopeEnum: PromotionProductScopeEnum,
|
||||
PromotionActivityStatusEnum: PromotionActivityStatusEnum,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
// 查询商品列表
|
||||
getSpuSimpleList().then(response => {
|
||||
this.productSpus = response.data
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getRewardActivityPage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
startAndEndTime: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
conditionType: PromotionConditionTypeEnum.PRICE.type,
|
||||
remark: undefined,
|
||||
productScope: PromotionProductScopeEnum.ALL.scope,
|
||||
productSpuIds: undefined,
|
||||
rules: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加满减送活动";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getRewardActivity(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
|
||||
this.open = true;
|
||||
this.title = "修改满减送活动";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
this.form.startTime = this.form.startAndEndTime[0];
|
||||
this.form.endTime = this.form.startAndEndTime[1];
|
||||
// TODO 芋艿:临时实现
|
||||
this.form.rules = [
|
||||
{
|
||||
limit: 1,
|
||||
discountPrice: 10,
|
||||
freeDelivery: true,
|
||||
point: 10,
|
||||
couponIds: [10, 20],
|
||||
couponCounts: [1, 2]
|
||||
}, {
|
||||
limit: 2,
|
||||
discountPrice: 20,
|
||||
freeDelivery: false,
|
||||
point: 20,
|
||||
couponIds: [30, 40],
|
||||
couponCounts: [3, 4]
|
||||
}
|
||||
];
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateRewardActivity(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createRewardActivity(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除满减送活动编号为"' + id + '"的数据项?').then(function() {
|
||||
return deleteRewardActivity(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 关闭按钮操作 */
|
||||
handleClose(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认关闭满减送活动编号为"' + id + '"的数据项?').then(function() {
|
||||
return closeRewardActivity(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("关闭成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,490 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
|
||||
label-width="68px">
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入秒杀活动名称" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)" :key="dict.value"
|
||||
:label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="参与场次" prop="timeId">
|
||||
<el-select v-model="queryParams.timeId" placeholder="请选择参与场次" clearable size="small">
|
||||
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['promotion:seckill-activity:create']">新增秒杀活动</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-menu" size="mini" @click="openSeckillTime"
|
||||
v-hasPermi="['promotion:seckill-activity:create']">管理参与场次</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="活动名称" align="center" prop="name" />
|
||||
<el-table-column label="活动状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="参与场次" prop="timeIds" width="250">
|
||||
<template slot-scope="scope">
|
||||
<span v-for="item in seckillTimeList" :key="item.id"
|
||||
v-if="scope.row.timeIds.includes(item.id)">
|
||||
<el-tag style="margin:4px;" size="small">{{ item.name }}</el-tag>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动开始时间" align="center" prop="startTime" width="190">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ "开始: " + parseTime(scope.row.startTime) }}</span>
|
||||
<span>{{ "结束: " + parseTime(scope.row.endTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="付款订单数" align="center" prop="orderCount" />
|
||||
<el-table-column label="付款人数" align="center" prop="userCount" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['promotion:seckill-activity:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-close" @click="handleClose(scope.row)"
|
||||
v-hasPermi="['promotion:seckill-activity:delete']">关闭</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['promotion:seckill-activity:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="1200px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入秒杀活动名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动时间" prop="startAndEndTime">
|
||||
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange"
|
||||
value-format="timestamp" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
style="width: 1080px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="form.sort" controls-position="right" :min="0" :max="10000">
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-form-item label="场次选择">
|
||||
<el-select v-model="form.timeIds" placeholder="请选择参与场次" clearable size="small" multiple filterable
|
||||
style="width: 880px">
|
||||
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.name + ': { ' }} {{ item.startTime }} -- {{ item.endTime +
|
||||
' }'
|
||||
}}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px"></span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品选择">
|
||||
<el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small" multiple filterable
|
||||
style="width: 880px" @change="changeFormSku">
|
||||
<el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name"
|
||||
:value="item.id">
|
||||
<span style="float: left">{{ item.spuName }} {{ item.name }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.price /
|
||||
100.0).toFixed(2)
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-row>
|
||||
<el-button type="primary" size="mini" @click="batchEditProduct('limitBuyCount')">限购</el-button>
|
||||
<el-button type="primary" size="mini" @click="batchEditProduct('seckillPrice')">秒杀价</el-button>
|
||||
<el-button type="primary" size="mini" @click="batchEditProduct('seckillStock')">秒杀库存</el-button>
|
||||
</el-row>
|
||||
<el-table v-loading="loading" ref="productsTable" :data="form.products">
|
||||
<el-table-column type="selection" width="55">
|
||||
</el-table-column>
|
||||
<el-table-column label="商品名称" align="center" width="200">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.spuName }} {{ scope.row.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品价格" align="center" prop="price">
|
||||
<template slot-scope="scope">
|
||||
¥{{ (scope.row.price / 100.0).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库存" align="center" prop="productStock" />
|
||||
<el-table-column label="限购(0为不限购)" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number v-model="scope.row.limitBuyCount" size="mini" :min="0" :max="10000">
|
||||
</el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="秒杀价(元)" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number v-model="scope.row.seckillPrice" size="mini" :precision="2" :min="0"
|
||||
:max="10000">
|
||||
</el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="秒杀库存" align="center" width="150" prop="seckillStock">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number v-model="scope.row.seckillStock" size="mini" :min="0" :max="10000">
|
||||
</el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete"
|
||||
@click="removeFormSku(scope.row.skuId)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSkuOptionList } from "@/api/mall/product/sku";
|
||||
import { createSeckillActivity, updateSeckillActivity, closeSeckillActivity, deleteSeckillActivity, getSeckillActivity, getSeckillActivityPage, exportSeckillActivityExcel } from "@/api/mall/promotion/seckillActivity";
|
||||
import { getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
|
||||
import { deepClone } from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "SeckillActivity",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 秒杀活动列表
|
||||
list: [],
|
||||
// 秒杀场次列表
|
||||
seckillTimeList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
timeId: null,
|
||||
createTime: [],
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
skuIds: [], // 选中的 SKU
|
||||
products: [], // 商品信息
|
||||
timeIds: [], //选中的秒杀场次id
|
||||
},
|
||||
// 商品 SKU 列表
|
||||
productSkus: [],
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{ required: true, message: "秒杀活动名称不能为空", trigger: "blur" }],
|
||||
status: [{ required: true, message: "活动状态不能为空", trigger: "blur" }],
|
||||
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
|
||||
sort: [{ required: true, message: "排序不能为空", trigger: "blur" }],
|
||||
timeIds: [{ required: true, message: "秒杀场次不能为空", trigger: "blur" }],
|
||||
totalPrice: [{ required: true, message: "订单实付金额,单位:分不能为空", trigger: "blur" }],
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
watch: {
|
||||
$route: 'getList'
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 从秒杀时段跳转过来并鞋带timeId参数进行查询
|
||||
const timeId = this.$route.params && this.$route.params.timeId;
|
||||
if (timeId) {
|
||||
this.queryParams.timeId = timeId
|
||||
}
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getSeckillActivityPage(this.queryParams).then(response => {
|
||||
console.log(response.data.list, "查询出的值");
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
if (timeId) {
|
||||
//查询完成后设置为空
|
||||
this.$route.params.timeId = undefined
|
||||
}
|
||||
// 获得 SKU 商品列表
|
||||
getSkuOptionList().then(response => {
|
||||
this.productSkus = response.data;
|
||||
});
|
||||
// 获取参与场次列表
|
||||
getSeckillTimeList().then(response => {
|
||||
this.seckillTimeList = response.data;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
remark: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
sort: undefined,
|
||||
timeIds: [],
|
||||
totalPrice: undefined,
|
||||
skuIds: [],
|
||||
products: [],
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/**打开秒杀场次管理页面 */
|
||||
openSeckillTime() {
|
||||
this.$tab.openPage("秒杀场次管理", "/promotion/seckill-time");
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加秒杀活动";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getSeckillActivity(id).then(response => {
|
||||
this.form = response.data;
|
||||
// 修改数据
|
||||
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
|
||||
this.form.skuIds = response.data.products.map(item => item.skuId);
|
||||
this.form.products.forEach(product => {
|
||||
// 获得对应的 SKU 信息
|
||||
const sku = this.productSkus.find(item => item.id === product.skuId);
|
||||
if (!sku) {
|
||||
return;
|
||||
}
|
||||
// 设置商品信息
|
||||
product.name = sku.name;
|
||||
product.spuName = sku.spuName;
|
||||
product.price = sku.price;
|
||||
product.productStock = sku.stock;
|
||||
this.$set(product, 'seckillStock', product.stock);
|
||||
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice / 100 : undefined;
|
||||
|
||||
});
|
||||
// 打开弹窗
|
||||
this.open = true;
|
||||
this.title = "修改限时折扣活动";
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 处理数据
|
||||
const data = deepClone(this.form);
|
||||
data.startTime = this.form.startAndEndTime[0];
|
||||
data.endTime = this.form.startAndEndTime[1];
|
||||
data.products.forEach(product => {
|
||||
product.stock = product.seckillStock;
|
||||
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice * 100 : undefined;
|
||||
});
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateSeckillActivity(data).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createSeckillActivity(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 关闭按钮操作 */
|
||||
handleClose(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认关闭秒杀活动编号为"' + id + '"的数据项?').then(function () {
|
||||
return closeSeckillActivity(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("关闭成功");
|
||||
}).catch(() => { });
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除秒杀活动编号为"' + id + '"的数据项?').then(function () {
|
||||
return deleteSeckillActivity(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => { });
|
||||
},
|
||||
/** 批量修改商品秒杀价,秒杀库存,每人限购数量 */
|
||||
batchEditProduct(editType) {
|
||||
const selectProducts = this.$refs.productsTable.selection;
|
||||
if (selectProducts.length === 0) {
|
||||
this.$modal.msgError("请选择需要修改的商品");
|
||||
return;
|
||||
}
|
||||
let promptTitle = '请输入';
|
||||
let regularPattern = /^[\s\S]*.*[^\s][\s\S]*$/; // 判断非空,且非空格
|
||||
//限购数
|
||||
if (editType === 'limitBuyCount') {
|
||||
promptTitle = '限购数';
|
||||
regularPattern = /^[0-9]*$/; //数字
|
||||
}
|
||||
//秒杀价
|
||||
if (editType === 'seckillPrice') {
|
||||
promptTitle = '秒杀价(元)';
|
||||
regularPattern = /^[0-9]+(\.[0-9]{1,2})?$/; // 有一位或两位小数的正数
|
||||
}
|
||||
//秒杀库存
|
||||
if (editType === 'seckillStock') {
|
||||
promptTitle = '秒杀库存';
|
||||
regularPattern = /^[0-9]*$/; //数字
|
||||
}
|
||||
|
||||
this.$prompt(promptTitle, '提示', {
|
||||
confirmButtonText: '保存',
|
||||
cancelButtonText: '取消',
|
||||
inputPattern: regularPattern,
|
||||
inputErrorMessage: promptTitle + '格式不正确'
|
||||
}).then(({ value }) => {
|
||||
if (editType === 'limitBuyCount') {
|
||||
selectProducts.forEach((item) => {
|
||||
item.limitBuyCount = value;
|
||||
})
|
||||
}
|
||||
if (editType === 'seckillPrice') {
|
||||
selectProducts.forEach((item) => {
|
||||
item.seckillPrice = value;
|
||||
})
|
||||
}
|
||||
if (editType === 'seckillStock') {
|
||||
selectProducts.forEach((item) => {
|
||||
item.seckillStock = value;
|
||||
})
|
||||
}
|
||||
}).catch();
|
||||
},
|
||||
/** 当 Form 的 SKU 发生变化时 */
|
||||
changeFormSku(skuIds) {
|
||||
// 处理【新增】
|
||||
skuIds.forEach(skuId => {
|
||||
// 获得对应的 SKU 信息
|
||||
const sku = this.productSkus.find(item => item.id === skuId);
|
||||
if (!sku) {
|
||||
return;
|
||||
}
|
||||
// 判断已存在,直接跳过
|
||||
const product = this.form.products.find(item => item.skuId === skuId);
|
||||
if (product) {
|
||||
return;
|
||||
}
|
||||
this.form.products.push({
|
||||
skuId: sku.id,
|
||||
name: sku.name,
|
||||
price: sku.price,
|
||||
productStock: sku.stock,
|
||||
spuId: sku.spuId,
|
||||
spuName: sku.spuName,
|
||||
limitBuyCount: 1,
|
||||
seckillStock: sku.stock,
|
||||
seckillPrice: sku.price,
|
||||
});
|
||||
});
|
||||
// 处理【移除】
|
||||
this.form.products.map((product, index) => {
|
||||
if (!skuIds.includes(product.skuId)) {
|
||||
this.form.products.splice(index, 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 移除 Form 的 SKU */
|
||||
removeFormSku(skuId) {
|
||||
this.form.skuIds.map((id, index) => {
|
||||
if (skuId === id) {
|
||||
this.form.skuIds.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.changeFormSku(this.form.skuIds);
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['promotion:seckill-time:create']">新增秒杀时段</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="秒杀时段名称" align="center" prop="name" />
|
||||
<el-table-column label="开始时间点" align="center" prop="startTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.startTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间点" align="center" prop="endTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.endTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="秒杀活动数量" align="center" prop="seckillActivityCount" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleOpenSeckillActivity(scope.row)">
|
||||
查看秒杀活动</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['promotion:seckill-time:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['promotion:seckill-time:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
|
||||
<el-form-item label="秒杀场次名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入秒杀时段名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="秒杀时间段" prop="startAndEndTime">
|
||||
<el-time-picker is-range v-model="form.startAndEndTime" range-separator="至" start-placeholder="开始时间"
|
||||
end-placeholder="结束时间" placeholder="选择时间范围" value-format="HH:mm:ss">
|
||||
</el-time-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createSeckillTime, updateSeckillTime, deleteSeckillTime, getSeckillTime, getSeckillTimePage, exportSeckillTimeExcel, getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
|
||||
import router from "@/router";
|
||||
import { deepClone } from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "SeckillTime",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
// total: 0,
|
||||
// 秒杀时段列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{ required: true, message: "秒杀时段名称不能为空", trigger: "blur" }],
|
||||
startAndEndTime: [{ required: true, message: "秒杀时间段不能为空", trigger: "blur" }],
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getSeckillTimeList().then(response => {
|
||||
this.list = response.data;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
startAndEndTime: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/**查看当前秒杀时段的秒杀活动 */
|
||||
handleOpenSeckillActivity(row) {
|
||||
router.push({ name: 'SeckillActivity', params: { timeId: row.id } })
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加秒杀时段";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getSeckillTime(id).then(response => {
|
||||
response.data.startAndEndTime = [response.data.startTime, response.data.endTime]
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改秒杀时段";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
console.log(valid, "是否通过");
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 处理数据
|
||||
const data = deepClone(this.form);
|
||||
data.startTime = this.form.startAndEndTime[0];
|
||||
data.endTime = this.form.startAndEndTime[1];
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateSeckillTime(data).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createSeckillTime(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除秒杀时段编号为"' + id + '"的数据项?').then(function () {
|
||||
return deleteSeckillTime(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => { });
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
228
yudao-ui-admin/src/views/mall/trade/afterSale/index.vue
Normal file
228
yudao-ui-admin/src/views/mall/trade/afterSale/index.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="商品名称" prop="spuName">
|
||||
<el-input v-model="queryParams.spuName" placeholder="请输入商品 SPU 名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="退款编号" prop="no">
|
||||
<el-input v-model="queryParams.no" placeholder="请输入退款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单编号" prop="orderNo">
|
||||
<el-input v-model="queryParams.orderNo" placeholder="请输入订单编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="售后状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择售后状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="售后方式" prop="way">
|
||||
<el-select v-model="queryParams.way" placeholder="请选择售后方式" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_WAY)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="售后类型" prop="type">
|
||||
<el-select v-model="queryParams.type" placeholder="请选择售后类型" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- Tab 选项:真正的内容在 Table -->
|
||||
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
|
||||
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
|
||||
</el-tabs>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="退款编号" align="center" prop="no" />
|
||||
<el-table-column label="订单编号" align="center" prop="orderNo" /> <!-- TODO 芋艿:未来要加个订单链接 -->
|
||||
<el-table-column label="商品信息" align="center" prop="spuName" width="auto" min-width="300">
|
||||
<!-- TODO @小红:样式不太对,辛苦改改 -->
|
||||
<!-- <div slot-scope="{ row }" class="goods-info">-->
|
||||
<!-- <img :src="row.picUrl"/>-->
|
||||
<!-- <span class="ellipsis-2" :title="row.name">{{row.name}}</span>-->
|
||||
<!-- </div>-->
|
||||
</el-table-column>
|
||||
<el-table-column label="订单金额" align="center" prop="refundPrice">
|
||||
<template v-slot="scope">
|
||||
<span>¥{{ (scope.row.refundPrice / 100.0).toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="买家" align="center" prop="user.nickname" /> <!-- TODO 芋艿:未来要加个会员链接 -->
|
||||
<el-table-column label="申请时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售后状态" align="center">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售后方式" align="center">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-thumb"
|
||||
>处理退款</el-button>
|
||||
<!-- @click="handleUpdate(scope.row)" v-hasPermi="['trade:after-sale:update']"-->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAfterSalePage } from "@/api/mall/trade/afterSale";
|
||||
import { datePickerOptions } from "@/utils/constants";
|
||||
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
|
||||
|
||||
export default {
|
||||
name: "AfterSale",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 交易售后列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
no: null,
|
||||
status: null,
|
||||
orderNo: null,
|
||||
spuName: null,
|
||||
createTime: [],
|
||||
way: null,
|
||||
type: null,
|
||||
},
|
||||
// Tab 筛选
|
||||
activeTab: 'all',
|
||||
statusTabs: [{
|
||||
label: '全部',
|
||||
value: 'all'
|
||||
}],
|
||||
// 静态变量
|
||||
datePickerOptions: datePickerOptions
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
// 设置 statuses 过滤
|
||||
for (const dict of getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)) {
|
||||
this.statusTabs.push({
|
||||
label: dict.label,
|
||||
value: dict.value
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getAfterSalePage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.activeTab = this.queryParams.status ? this.queryParams.status : 'all'; // 处理 tab
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.activeTab = 'all'; // 处理 tab
|
||||
this.handleQuery();
|
||||
},
|
||||
/** tab 切换 */
|
||||
tabClick(tab) {
|
||||
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
|
||||
this.getList();
|
||||
},
|
||||
goToDetail (row) {
|
||||
this.$router.push({ path: '/mall/trade/order/detail', query: { orderNo: row.orderNo }})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .table-wrapper {
|
||||
.el-table__row{
|
||||
.el-table__cell {
|
||||
border-bottom: none;
|
||||
.cell{
|
||||
.el-table {
|
||||
.el-table__row {
|
||||
>.el-table__cell {
|
||||
.goods-info{
|
||||
display: flex;
|
||||
img{
|
||||
margin-right: 10px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 1px solid #e2e2e2;
|
||||
}
|
||||
}
|
||||
.ellipsis-2 {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
-webkit-line-clamp: 2; /* 要显示的行数 */
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
line-height: 22px !important;
|
||||
max-height: 44px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
279
yudao-ui-admin/src/views/mall/trade/order/detail.vue
Normal file
279
yudao-ui-admin/src/views/mall/trade/order/detail.vue
Normal file
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="app-container order-detail-page">
|
||||
<!-- 订单信息 -->
|
||||
<el-descriptions title="订单信息">
|
||||
<el-descriptions-item label="订单号">{{ order.no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="配送方式">物流配送</el-descriptions-item> <!-- TODO 芋艿:待实现 -->
|
||||
<el-descriptions-item label="营销活动">物流配送</el-descriptions-item> <!-- TODO 芋艿:待实现 -->
|
||||
<el-descriptions-item label="订单类型">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="order.type" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收货人">{{ order.receiverName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="买家留言">{{ order.userRemark }}</el-descriptions-item>
|
||||
<el-descriptions-item label="订单来源">
|
||||
<dict-tag :type="DICT_TYPE.TERMINAL" :value="order.terminal" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ order.receiverMobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="商家备注">{{ order.remark }}</el-descriptions-item>
|
||||
<el-descriptions-item label="支付单号">{{ order.payOrderId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="付款方式">
|
||||
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE" :value="order.payChannelCode" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="买家">{{ order.user.nickname }}</el-descriptions-item> <!-- TODO 芋艿:待实现:跳转会员 -->
|
||||
<el-descriptions-item label="收货地址">
|
||||
{{ order.receiverAreaName }} {{ order.receiverDetailAddress }}
|
||||
<el-link v-clipboard:copy="order.receiverAreaName + ' ' + order.receiverDetailAddress"
|
||||
v-clipboard:success="clipboardSuccess" icon="el-icon-document-copy" type="primary"/>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 订单状态 -->
|
||||
<el-descriptions title="订单状态" :column="1">
|
||||
<el-descriptions-item label="订单状态">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="order.status" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="no-colon">
|
||||
<el-button type="primary" size="small">调整价格</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
<el-button type="primary" size="small">备注</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
<el-button type="primary" size="small">发货</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
<el-button type="primary" size="small">关闭订单</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
<el-button type="primary" size="small">修改地址</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
<el-button type="primary" size="small">打印电子面单</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
<el-button type="primary" size="small">打印发货单</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
<el-button type="primary" size="small">确认收货</el-button> <!-- TODO 芋艿:待实现 -->
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提醒" label-style="color: red">
|
||||
买家付款成功后,货款将直接进入您的商户号(微信、支付宝)<br />
|
||||
请及时关注你发出的包裹状态,确保可以配送至买家手中 <br />
|
||||
如果买家表示没收到货或货物有问题,请及时联系买家处理,友好协商
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 物流信息 TODO -->
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<el-descriptions title="商品信息" :column="6">
|
||||
<el-descriptions-item labelClassName="no-colon">
|
||||
<el-table :data="order.items" border>
|
||||
<el-table-column prop="spuName" label="商品" width="700">
|
||||
<template slot-scope="{ row }">
|
||||
{{row.spuName}}
|
||||
<el-tag size="medium" v-for="property in row.properties" :key="property.propertyId">
|
||||
{{property.propertyName}}:{{property.valueName}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="originalUnitPrice" label="单价(元)" width="180">
|
||||
<template slot-scope="{ row }">
|
||||
¥{{ (row.originalUnitPrice / 100.0).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="count" label="数量" width="180"/>
|
||||
<el-table-column prop="originalPrice" label="小计(元)" width="180">
|
||||
<template slot-scope="{ row }">
|
||||
¥{{ (row.originalPrice / 100.0).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="afterSaleStatus" label="退款状态">
|
||||
<template slot-scope="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS" :value="row.afterSaleStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-for="(item,index) in 5" label-class-name="no-colon" :key="item" /> <!-- 占位 -->
|
||||
<el-descriptions-item label="商品总额">¥{{ (order.originalPrice / 100.0).toFixed(2) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运费金额">¥{{ (order.deliveryPrice / 100.0).toFixed(2) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="订单调价">¥{{ (order.adjustPrice / 100.0).toFixed(2) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="商品优惠" label-style="color: red">
|
||||
¥{{ ((order.originalPrice - order.originalPrice) / 100.0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="订单优惠" label-style="color: red">
|
||||
¥{{ (order.discountPrice / 100.0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="积分抵扣" label-style="color: red">
|
||||
¥{{ (order.pointPrice / 100.0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-for="(item,index) in 5" label-class-name="no-colon" :key="item" /> <!-- 占位 -->
|
||||
<el-descriptions-item label="应付金额">
|
||||
¥{{ (order.payPrice / 100.0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<template v-for="(group, index) in detailGroups">
|
||||
<el-descriptions v-bind="group.groupProps" :key="`group_${index}`" :title="group.title">
|
||||
|
||||
<!-- 订单操作日志 -->
|
||||
<el-descriptions-item v-if="group.key === 'orderLog'" labelClassName="no-colon">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(activity, index) in detailInfo[group.key]"
|
||||
:key="index"
|
||||
:timestamp="activity.timestamp"
|
||||
>
|
||||
{{activity.content}}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-descriptions-item>
|
||||
|
||||
<!-- 物流信息 -->
|
||||
<el-descriptions-item v-if="group.key === 'expressInfo'" labelClassName="no-colon">
|
||||
<el-tabs type="card">
|
||||
<!-- 循环包裹物流信息 -->
|
||||
<el-tab-pane v-for="(pkgInfo, pInIdx) in detailInfo[group.key]" :key="`pkgInfo_${pInIdx}`" :label="pkgInfo.label">
|
||||
<!-- 包裹详情 -->
|
||||
<el-descriptions>
|
||||
<el-descriptions-item v-for="(pkgChild, pkgCIdx) in group.children" v-bind="pkgChild.childProps" :key="`pkgChild_${pkgCIdx}`" :label="pkgChild.label">
|
||||
<!-- 包裹商品列表 -->
|
||||
<template v-if="pkgChild.valueKey === 'goodsList' && pkgInfo[pkgChild.valueKey]">
|
||||
<div v-for="(goodInfo, goodInfoIdx) in pkgInfo[pkgChild.valueKey]" :key="`goodInfo_${goodInfoIdx}`" style="display: flex;">
|
||||
<el-image
|
||||
style="width: 100px;height: 100px;flex: none"
|
||||
:src="goodInfo.imgUrl">
|
||||
</el-image>
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item labelClassName="no-colon">{{goodInfo.name}}</el-descriptions-item>
|
||||
<el-descriptions-item label="数量">{{goodInfo.count}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 包裹物流详情 -->
|
||||
<el-timeline v-else-if="pkgChild.valueKey==='wlxq'">
|
||||
<el-timeline-item
|
||||
v-for="(activity, index) in pkgInfo[pkgChild.valueKey]"
|
||||
:key="index"
|
||||
:timestamp="activity.timestamp"
|
||||
>
|
||||
{{activity.content}}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
|
||||
<template v-else>
|
||||
{{pkgInfo[pkgChild.valueKey]}}
|
||||
</template>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getOrderDetail } from "@/api/mall/trade/order";
|
||||
|
||||
export default {
|
||||
name: "detail",
|
||||
data () {
|
||||
return {
|
||||
detailGroups: [
|
||||
{
|
||||
title: '物流信息',
|
||||
key: 'expressInfo',
|
||||
children: [
|
||||
{ label: '发货时间', valueKey: 'fhsj'},
|
||||
{ label: '物流公司', valueKey: 'wlgs'},
|
||||
{ label: '运单号', valueKey: 'ydh'},
|
||||
{ label: '物流状态', valueKey: 'wlzt', childProps: { span: 3 }},
|
||||
{ label: '物流详情', valueKey: 'wlxq'}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '订单操作日志',
|
||||
key: 'orderLog'
|
||||
}
|
||||
],
|
||||
detailInfo: {
|
||||
expressInfo: [ // 物流信息
|
||||
{
|
||||
label: '包裹1',
|
||||
name: 'bg1',
|
||||
fhsj: '2022-11-03 16:50:45',
|
||||
wlgs: '极兔',
|
||||
ydh: '2132123',
|
||||
wlzt: '不支持此快递公司',
|
||||
wlxq: [
|
||||
{
|
||||
content: '正在派送途中,请您准备签收(派件人:王涛,电话:13854563814)',
|
||||
timestamp: '2018-04-15 15:00:16'
|
||||
},
|
||||
{
|
||||
content: '快件到达 【烟台龙口东江村委营业点】',
|
||||
timestamp: '2018-04-13 14:54:19'
|
||||
},
|
||||
{
|
||||
content: '快件已发车',
|
||||
timestamp: '2018-04-11 12:55:52'
|
||||
},
|
||||
{
|
||||
content: '快件已发车',
|
||||
timestamp: '2018-04-11 12:55:52'
|
||||
},
|
||||
{
|
||||
content: '快件已发车',
|
||||
timestamp: '2018-04-11 12:55:52'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
orderLog: [ // 订单操作日志
|
||||
{
|
||||
content: '买家【乌鸦】关闭了订单',
|
||||
timestamp: '2018-04-15 15:00:16'
|
||||
},
|
||||
{
|
||||
content: '买家【乌鸦】下单了',
|
||||
timestamp: '2018-04-15 15:00:16'
|
||||
}
|
||||
],
|
||||
goodsInfo: [] // 商品详情tableData
|
||||
},
|
||||
order: {
|
||||
items: [],
|
||||
user: {},
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getOrderDetail(this.$route.query.id).then(res => {
|
||||
this.order = res.data
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
clipboardSuccess() {
|
||||
this.$modal.msgSuccess("复制成功");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-descriptions){
|
||||
&:not(:nth-child(1)) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.el-descriptions__title{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&::before{
|
||||
content: '';
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
width: 3px;
|
||||
height: 20px;
|
||||
background-color: #409EFF;
|
||||
}
|
||||
}
|
||||
.el-descriptions-item__container{
|
||||
margin: 0 10px;
|
||||
.no-colon{
|
||||
margin: 0;
|
||||
&::after{
|
||||
content: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
279
yudao-ui-admin/src/views/mall/trade/order/index.vue
Normal file
279
yudao-ui-admin/src/views/mall/trade/order/index.vue
Normal file
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<!-- TODO: inline 看看是不是需要; v-show= 那块逻辑还是要的 -->
|
||||
<el-row :gutter="20">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-form-item label="搜索方式" prop="searchValue">
|
||||
<el-input v-model="queryParams.searchValue" style="width: 240px">
|
||||
<el-select v-model="queryParams.searchType" slot="prepend" style="width: 100px">
|
||||
<el-option v-for="dict in searchTypes" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-form-item label="订单类型" prop="type">
|
||||
<el-select v-model="queryParams.type" clearable style="width: 240px">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-form-item label="订单状态" prop="status">
|
||||
<el-select v-model="queryParams.status" clearable style="width: 240px">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-form-item label="订单来源" prop="terminal">
|
||||
<el-select v-model="queryParams.terminal" clearable style="width: 240px">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TERMINAL)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-form-item label="支付方式" prop="payChannelCode">
|
||||
<el-select v-model="queryParams.payChannelCode" clearable style="width: 240px">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-form-item label="下单时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24" style="line-height: 32px">
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- tab切换 -->
|
||||
<!-- TODO @小程:看看能不能往上挪 -40px,和【隐藏搜索】【刷新】对齐 -->
|
||||
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick">
|
||||
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value">
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list" :show-header="false" class="order-table">
|
||||
<el-table-column>
|
||||
<template slot-scope="{ row }">
|
||||
<el-row type="flex" align="middle">
|
||||
<el-col :span="5">
|
||||
订单号:{{row.no}}
|
||||
<el-popover title="支付单号:" :content="row.payOrderId + ''" placement="right" width="200" trigger="click">
|
||||
<el-button slot="reference" type="text">更多</el-button>
|
||||
</el-popover>
|
||||
</el-col>
|
||||
<el-col :span="5">下单时间:{{ parseTime(row.createTime) }}</el-col>
|
||||
<el-col :span="4">订单来源:
|
||||
<dict-tag :type="DICT_TYPE.TERMINAL" :value="row.terminal" />
|
||||
</el-col>
|
||||
<el-col :span="4">支付方式:
|
||||
<dict-tag v-if="row.payChannelCode" :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE" :value="row.payChannelCode" />
|
||||
<span v-else>未支付</span>
|
||||
</el-col>
|
||||
<el-col :span="6" align="right">
|
||||
<el-button type="text" @click="goToDetail(row)">详情</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 订单下的商品 -->
|
||||
<el-table :data="row.items" border :show-header="true">
|
||||
<el-table-column label="商品" prop="goods" header-align="center" width="auto" min-width="300">
|
||||
<template slot-scope="{ row, $index }">
|
||||
<div class="goods-info">
|
||||
<img :src="row.picUrl"/>
|
||||
<span class="ellipsis-2" :title="row.spuName">{{row.spuName}}</span>
|
||||
<!-- TODO @小程:下面是商品属性,想当度一行,放在商品名下面 -->
|
||||
<el-tag size="medium" v-for="property in row.properties" :key="property.propertyId">
|
||||
{{property.propertyName}}:{{property.valueName}}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价(元)/数量" prop="fee" align="center" width="115">
|
||||
<template slot-scope="{ row }">
|
||||
<div>¥{{ (row.originalUnitPrice / 100.0).toFixed(2) }}</div>
|
||||
<div>{{row.count}} 件</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @小程:这里应该是一个订单下,多个商品,只展示订单上的总金额,就是 order.payPrice -->
|
||||
<el-table-column label="实付金额(元)" prop="amount" align="center" width="100"/>
|
||||
<!-- TODO @小程:这里应该是一个订单下,多个商品,只展示订单上的收件信息;使用 order.receiverXXX 开头的字段 -->
|
||||
<el-table-column label="买家/收货人" prop="buyer" header-align="center" width="auto" min-width="300">
|
||||
<template slot-scope="{ row }">
|
||||
<!-- TODO @芋艿:以后增加一个会员详情界面 -->
|
||||
<div>{{row.buyer}}</div>
|
||||
<div>{{row.receiver}}{{row.tel}}</div>
|
||||
<div class="ellipsis-2" :title="row.address">{{row.address}}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @小程:这里应该是一个订单下,多个商品,交易状态是统一的;使用 order.status 字段 -->
|
||||
<el-table-column label="交易状态" prop="status" align="center" width="100"/>
|
||||
</el-table>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getOrderPage } from "@/api/mall/trade/order";
|
||||
import { datePickerOptions } from "@/utils/constants";
|
||||
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
|
||||
|
||||
export default {
|
||||
name: "index",
|
||||
data () {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 交易售后列表
|
||||
list: [],
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
searchType: 'no',
|
||||
searchValue: '',
|
||||
type: null,
|
||||
status: null,
|
||||
payChannelCode: null,
|
||||
createTime: [],
|
||||
},
|
||||
// Tab 筛选
|
||||
activeTab: 'all',
|
||||
statusTabs: [{
|
||||
label: '全部',
|
||||
value: 'all'
|
||||
}],
|
||||
// 静态变量
|
||||
datePickerOptions: datePickerOptions,
|
||||
searchTypes: [
|
||||
{ label: '订单号', value: 'no' },
|
||||
{ label: '会员编号', value: 'userId' },
|
||||
{ label: '会员昵称', value: 'userNickname' },
|
||||
{ label: '会员手机号', value: 'userMobile' },
|
||||
{ label: '收货人姓名', value: 'receiverName' },
|
||||
{ label: '收货人手机号码', value: 'receiverMobile' },
|
||||
],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
// 设置 statuses 过滤
|
||||
for (const dict of getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)) {
|
||||
this.statusTabs.push({
|
||||
label: dict.label,
|
||||
value: dict.value
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getOrderPage({
|
||||
...this.queryParams,
|
||||
searchType: undefined,
|
||||
searchValue: undefined,
|
||||
no: this.queryParams.searchType === 'no' ? this.queryParams.searchValue : undefined,
|
||||
userId: this.queryParams.searchType === 'userId' ? this.queryParams.searchValue : undefined,
|
||||
userNickname: this.queryParams.searchType === 'userNickname' ? this.queryParams.searchValue : undefined,
|
||||
userMobile: this.queryParams.searchType === 'userMobile' ? this.queryParams.searchValue : undefined,
|
||||
receiverName: this.queryParams.searchType === 'receiverName' ? this.queryParams.searchValue : undefined,
|
||||
receiverMobile: this.queryParams.searchType === 'receiverMobile' ? this.queryParams.searchValue : undefined,
|
||||
}).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.activeTab = this.queryParams.status ? this.queryParams.status : 'all'; // 处理 tab
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** tab 切换 */
|
||||
tabClick(tab) {
|
||||
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
|
||||
this.getList();
|
||||
},
|
||||
goToDetail (row) {
|
||||
this.$router.push({ path: '/trade/order/detail', query: { id: row.id }})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .order-table{
|
||||
border-bottom: none;
|
||||
&::before{
|
||||
height: 0;
|
||||
}
|
||||
.el-table__row{
|
||||
.el-table__cell{
|
||||
border-bottom: none;
|
||||
.cell{
|
||||
.el-table {
|
||||
.el-table__row{
|
||||
>.el-table__cell{
|
||||
.goods-info{
|
||||
display: flex;
|
||||
img{
|
||||
margin-right: 10px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 1px solid #e2e2e2;
|
||||
}
|
||||
}
|
||||
.ellipsis-2{
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
-webkit-line-clamp: 2; /* 要显示的行数 */
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
line-height: 22px !important;
|
||||
max-height: 44px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
289
yudao-ui-admin/src/views/mp/account/index.vue
Normal file
289
yudao-ui-admin/src/views/mp/account/index.vue
Normal file
@@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable
|
||||
@keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['mp:account:create']">新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="名称" align="center" prop="name"/>
|
||||
<el-table-column label="微信号" align="center" prop="account" width="180"/>
|
||||
<el-table-column label="appId" align="center" prop="appId" width="180"/>
|
||||
<!-- <el-table-column label="appSecret" align="center" prop="appSecret" width="180"/>-->
|
||||
<!-- <el-table-column label="token" align="center" prop="token"/>-->
|
||||
<!-- <el-table-column label="消息加解密密钥" align="center" prop="aesKey"/>-->
|
||||
<el-table-column label="服务器地址(URL)" align="center" prop="appId" width="360">
|
||||
<template v-slot="scope">
|
||||
{{ 'http://服务端地址/mp/open/' + scope.row.appId }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="二维码" align="center" prop="qrCodeUrl">
|
||||
<template v-slot="scope">
|
||||
<img v-if="scope.row.qrCodeUrl" :src="scope.row.qrCodeUrl" alt="二维码" style="height: 100px;" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark"/>
|
||||
<!-- <el-table-column label="创建时间" align="center" prop="createTime" width="180">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.createTime) }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['mp:account:update']">修改
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['mp:account:delete']">删除
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleGenerateQrCode(scope.row)"
|
||||
v-hasPermi="['mp:account:qr-code']">生成二维码
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-share" @click="handleCleanQuota(scope.row)"
|
||||
v-hasPermi="['mp:account:clear-quota']">清空 API 配额
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信号" prop="account">
|
||||
<span slot="label">
|
||||
<el-tooltip content="在微信公众平台(mp.weixin.qq.com)的菜单 [设置与开发 - 公众号设置 - 账号详情] 中能找到「微信号」" placement="top">
|
||||
<i class="el-icon-question" />
|
||||
</el-tooltip>
|
||||
微信号
|
||||
</span>
|
||||
<el-input v-model="form.account" placeholder="请输入微信号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="appId" prop="appId">
|
||||
<span slot="label">
|
||||
<el-tooltip content="在微信公众平台(mp.weixin.qq.com)的菜单 [设置与开发 - 公众号设置 - 基本设置] 中能找到「开发者ID(AppID)」" placement="top">
|
||||
<i class="el-icon-question" />
|
||||
</el-tooltip>
|
||||
appId
|
||||
</span>
|
||||
<el-input v-model="form.appId" placeholder="请输入公众号 appId"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="appSecret" prop="appSecret">
|
||||
<span slot="label">
|
||||
<el-tooltip content="在微信公众平台(mp.weixin.qq.com)的菜单 [设置与开发 - 公众号设置 - 基本设置] 中能找到「开发者密码(AppSecret)」" placement="top">
|
||||
<i class="el-icon-question" />
|
||||
</el-tooltip>
|
||||
appSecret
|
||||
</span>
|
||||
<el-input v-model="form.appSecret" placeholder="请输入公众号 appSecret"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="token" prop="token">
|
||||
<el-input v-model="form.token" placeholder="请输入公众号token"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="消息加解密密钥" prop="aesKey">
|
||||
<el-input v-model="form.aesKey" placeholder="请输入消息加解密密钥"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
clearAccountQuota,
|
||||
createAccount,
|
||||
deleteAccount,
|
||||
generateAccountQrCode,
|
||||
getAccount,
|
||||
getAccountPage,
|
||||
updateAccount
|
||||
} from '@/api/mp/account'
|
||||
|
||||
export default {
|
||||
name: 'mpAccount',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 公众号账号列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
account: null,
|
||||
appId: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{required: true, message: '名称不能为空', trigger: 'blur'}],
|
||||
account: [{required: true, message: '公众号账号不能为空', trigger: 'blur'}],
|
||||
appId: [{required: true, message: '公众号 appId 不能为空', trigger: 'blur'}],
|
||||
appSecret: [{required: true, message: '公众号密钥不能为空', trigger: 'blur'}],
|
||||
token: [{required: true, message: '公众号 token 不能为空', trigger: 'blur'}],
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams}
|
||||
// 执行查询
|
||||
getAccountPage(params).then(response => {
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false
|
||||
this.reset()
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
account: undefined,
|
||||
appId: undefined,
|
||||
appSecret: undefined,
|
||||
token: undefined,
|
||||
aesKey: undefined,
|
||||
remark: undefined,
|
||||
}
|
||||
this.resetForm('form')
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRangeCreateTime = []
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.open = true
|
||||
this.title = '添加公众号账号'
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const id = row.id
|
||||
getAccount(id).then(response => {
|
||||
this.form = response.data
|
||||
this.open = true
|
||||
this.title = '修改公众号账号'
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateAccount(this.form).then(response => {
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
return
|
||||
}
|
||||
// 添加的提交
|
||||
createAccount(this.form).then(response => {
|
||||
this.$modal.msgSuccess('新增成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id
|
||||
this.$modal.confirm('是否确认删除公众号账号编号为"' + row.name + '"的数据项?').then(function () {
|
||||
return deleteAccount(id)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
/** 生成二维码的按钮操作 */
|
||||
handleGenerateQrCode(row) {
|
||||
const id = row.id
|
||||
this.$modal.confirm('是否确认生成公众号账号编号为"' + row.name + '"的二维码?').then(function () {
|
||||
return generateAccountQrCode(id)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess('生成二维码成功')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
/** 清空二维码 API 配额的按钮操作 */
|
||||
handleCleanQuota(row) {
|
||||
const id = row.id
|
||||
this.$modal.confirm('是否确认清空生成公众号账号编号为"' + row.name + '"的 API 配额?').then(function () {
|
||||
return clearAccountQuota(id)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('清空 API 配额成功')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
382
yudao-ui-admin/src/views/mp/autoReply/index.vue
Normal file
382
yudao-ui-admin/src/views/mp/autoReply/index.vue
Normal file
@@ -0,0 +1,382 @@
|
||||
<!--
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 www.joolun.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
芋道源码:
|
||||
① 移除 avue 框架,使用 element-ui 重写
|
||||
② 重写代码,保持和现有项目保持一致
|
||||
-->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- tab 切换 -->
|
||||
<el-tabs v-model="type" @tab-click="handleClick">
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['mp:auto-reply:create']" v-if="type !== '1' || list.length <= 0">新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
<!-- tab 项 -->
|
||||
<el-tab-pane name="1">
|
||||
<span slot="label"><i class="el-icon-star-off"></i> 关注时回复</span>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="2">
|
||||
<span slot="label"><i class="el-icon-chat-line-round"></i> 消息回复</span>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="3">
|
||||
<span slot="label"><i class="el-icon-news"></i> 关键词回复</span>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="请求消息类型" align="center" prop="requestMessageType" v-if="type === '2'" />
|
||||
<el-table-column label="关键词" align="center" prop="requestKeyword" v-if="type === '3'" />
|
||||
<el-table-column label="匹配类型" align="center" prop="requestMatch" v-if="type === '3'">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH" :value="scope.row.requestMatch"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回复消息类型" align="center">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.MP_MESSAGE_TYPE" :value="scope.row.responseMessageType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回复内容" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.responseMessageType === 'text'">{{ scope.row.responseContent }}</div>
|
||||
<div v-else-if="scope.row.responseMessageType === 'voice'">
|
||||
<wx-voice-player :url="scope.row.responseMediaUrl" />
|
||||
</div>
|
||||
<div v-else-if="scope.row.responseMessageType === 'image'">
|
||||
<a target="_blank" :href="scope.row.responseMediaUrl">
|
||||
<img :src="scope.row.responseMediaUrl" style="width: 100px">
|
||||
</a>
|
||||
</div>
|
||||
<div v-else-if="scope.row.responseMessageType === 'video' || scope.row.responseMessageType === 'shortvideo'">
|
||||
<wx-video-player :url="scope.row.responseMediaUrl" style="margin-top: 10px" />
|
||||
</div>
|
||||
<div v-else-if="scope.row.responseMessageType === 'news'">
|
||||
<wx-news :articles="scope.row.responseArticles" />
|
||||
</div>
|
||||
<div v-else-if="scope.row.responseMessageType === 'music'">
|
||||
<wx-music :title="scope.row.responseTitle" :description="scope.row.responseDescription"
|
||||
:thumb-media-url="scope.row.responseThumbMediaUrl"
|
||||
:music-url="scope.row.responseMusicUrl" :hq-music-url="scope.row.responseHqMusicUrl" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['mp:auto-reply:update']">修改
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['mp:auto-reply:delete']">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 添加或修改自动回复的对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="消息类型" prop="requestMessageType" v-if="type === '2'">
|
||||
<el-select v-model="form.requestMessageType" placeholder="请选择">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"
|
||||
v-if="requestMessageTypes.includes(dict.value)"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="匹配类型" prop="requestMatch" v-if="type === '3'">
|
||||
<el-select v-model="form.requestMatch" placeholder="请选择匹配类型" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH)"
|
||||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键词" prop="requestKeyword" v-if="type === '3'">
|
||||
<el-input v-model="form.requestKeyword" placeholder="请输入内容" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="回复消息">
|
||||
<wx-reply-select :objData="objData" v-if="hackResetWxReplySelect" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
|
||||
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
|
||||
import WxMsg from '@/views/mp/components/wx-msg/main.vue';
|
||||
import WxLocation from '@/views/mp/components/wx-location/main.vue';
|
||||
import WxMusic from '@/views/mp/components/wx-music/main.vue';
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue';
|
||||
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updateAutoReply } from "@/api/mp/autoReply";
|
||||
|
||||
export default {
|
||||
name: 'mpAutoReply',
|
||||
components: {
|
||||
WxVideoPlayer,
|
||||
WxVoicePlayer,
|
||||
WxMsg,
|
||||
WxLocation,
|
||||
WxMusic,
|
||||
WxNews,
|
||||
WxReplySelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// tab 类型(1、关注时回复;2、消息回复;3、关键词回复)
|
||||
type: '3',
|
||||
// 允许选择的请求消息类型
|
||||
requestMessageTypes: ['text', 'image', 'voice', 'video', 'shortvideo', 'location', 'link'],
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 自动回复列表
|
||||
list: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
accountId: undefined,
|
||||
},
|
||||
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 回复消息
|
||||
objData: {
|
||||
type : 'text'
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
requestKeyword: [{ required: true, message: "请求的关键字不能为空", trigger: "blur" }],
|
||||
requestMatch: [{ required: true, message: "请求的关键字的匹配不能为空", trigger: "blur" }],
|
||||
},
|
||||
hackResetWxReplySelect: false, // 重置 WxReplySelect 组件,解决无法清除的问题
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.queryParams.accountId) {
|
||||
this.$message.error('未选中公众号,无法查询自动回复')
|
||||
return false
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
// 处理查询参数
|
||||
let params = {
|
||||
...this.queryParams,
|
||||
type: this.type
|
||||
}
|
||||
// 执行查询
|
||||
getAutoReplyPage(params).then(response => {
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
this.type = tab.name
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
/** 新增按钮操作 */
|
||||
handleAdd(){
|
||||
this.reset();
|
||||
this.resetEditor();
|
||||
// 打开表单,并设置初始化
|
||||
this.open = true
|
||||
this.title = '新增自动回复';
|
||||
this.objData = {
|
||||
type : 'text',
|
||||
accountId: this.queryParams.accountId,
|
||||
}
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
this.resetEditor();
|
||||
const id = row.id;
|
||||
getAutoReply(id).then(response => {
|
||||
// 设置属性
|
||||
this.form = {...response.data}
|
||||
this.$delete(this.form, 'responseMessageType');
|
||||
this.$delete(this.form, 'responseContent');
|
||||
this.$delete(this.form, 'responseMediaId');
|
||||
this.$delete(this.form, 'responseMediaUrl');
|
||||
this.$delete(this.form, 'responseDescription');
|
||||
this.$delete(this.form, 'responseArticles');
|
||||
this.objData = {
|
||||
type: response.data.responseMessageType,
|
||||
accountId: this.queryParams.accountId,
|
||||
content: response.data.responseContent,
|
||||
mediaId: response.data.responseMediaId,
|
||||
url: response.data.responseMediaUrl,
|
||||
title: response.data.responseTitle,
|
||||
description: response.data.responseDescription,
|
||||
thumbMediaId: response.data.responseThumbMediaId,
|
||||
thumbMediaUrl: response.data.responseThumbMediaUrl,
|
||||
articles: response.data.responseArticles,
|
||||
musicUrl: response.data.responseMusicUrl,
|
||||
hqMusicUrl: response.data.responseHqMusicUrl,
|
||||
}
|
||||
|
||||
// 打开表单
|
||||
this.open = true
|
||||
this.title = '修改自动回复';
|
||||
})
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 处理回复消息
|
||||
const form = {...this.form};
|
||||
form.responseMessageType = this.objData.type;
|
||||
form.responseContent = this.objData.content;
|
||||
form.responseMediaId = this.objData.mediaId;
|
||||
form.responseMediaUrl = this.objData.url;
|
||||
form.responseTitle = this.objData.title;
|
||||
form.responseDescription = this.objData.description;
|
||||
form.responseThumbMediaId = this.objData.thumbMediaId;
|
||||
form.responseThumbMediaUrl = this.objData.thumbMediaUrl;
|
||||
form.responseArticles = this.objData.articles;
|
||||
form.responseMusicUrl = this.objData.musicUrl;
|
||||
form.responseHqMusicUrl = this.objData.hqMusicUrl;
|
||||
|
||||
if (this.form.id !== undefined) {
|
||||
updateAutoReply(form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
} else {
|
||||
createAutoReply(form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
accountId: this.queryParams.accountId,
|
||||
type: this.type,
|
||||
requestKeyword: undefined,
|
||||
requestMatch: this.type === '3' ? 1 : undefined,
|
||||
requestMessageType: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单 Editor 重置
|
||||
resetEditor() {
|
||||
this.hackResetWxReplySelect = false // 销毁组件
|
||||
this.$nextTick(() => {
|
||||
this.hackResetWxReplySelect = true // 重建组件
|
||||
})
|
||||
},
|
||||
handleDelete: function(row) {
|
||||
const ids = row.id;
|
||||
this.$modal.confirm('是否确认删除此数据?').then(function() {
|
||||
return deleteAutoReply(ids);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
230
yudao-ui-admin/src/views/mp/components/wx-editor/WxEditor.vue
Normal file
230
yudao-ui-admin/src/views/mp/components/wx-editor/WxEditor.vue
Normal file
@@ -0,0 +1,230 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
① 调整 uploadData 属性,只需要传入 accountId 即可
|
||||
-->
|
||||
<template>
|
||||
<div id="wxEditor">
|
||||
<div v-loading="quillUpdateImg" element-loading-text="请稍等,图片上传中">
|
||||
<!-- 图片上传组件辅助-->
|
||||
<el-upload class="avatar-uploader" name="file" :action="actionUrl" :headers="headers"
|
||||
:show-file-list="false" :data="uploadData"
|
||||
:on-success="uploadSuccess" :on-error="uploadError" :before-upload="beforeUpload">
|
||||
</el-upload>
|
||||
<quill-editor class="editor" v-model="content" ref="myQuillEditor" :options="editorOption"
|
||||
@change="onEditorChange($event)">
|
||||
</quill-editor>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 工具栏配置
|
||||
const toolbarOptions = [
|
||||
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
|
||||
["blockquote", "code-block"], // 引用 代码块
|
||||
[{ header: 1 }, { header: 2 }], // 1、2 级标题
|
||||
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
|
||||
[{ script: "sub" }, { script: "super" }], // 上标/下标
|
||||
[{ indent: "-1" }, { indent: "+1" }], // 缩进
|
||||
// [{'direction': 'rtl'}], // 文本方向
|
||||
[{ size: ["small", false, "large", "huge"] }], // 字体大小
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
||||
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
||||
[{ font: [] }], // 字体种类
|
||||
[{ align: [] }], // 对齐方式
|
||||
["clean"], // 清除文本格式
|
||||
["link", "image", "video"] // 链接、图片、视频
|
||||
]
|
||||
|
||||
import { quillEditor } from "vue-quill-editor"
|
||||
import "quill/dist/quill.core.css"
|
||||
import "quill/dist/quill.snow.css"
|
||||
import "quill/dist/quill.bubble.css"
|
||||
import { getAccessToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/* 公众号账号编号 */
|
||||
accountId: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
/* 编辑器的内容 */
|
||||
value: {
|
||||
type: String
|
||||
},
|
||||
/* 图片大小 */
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 4000 // kb
|
||||
}
|
||||
},
|
||||
name: 'wxEditor',
|
||||
components: {
|
||||
quillEditor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editorType: '1',
|
||||
content: this.value.replace(/data-src/g, "src"),
|
||||
|
||||
quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
|
||||
editorOption: {
|
||||
theme: "snow", // or 'bubble'
|
||||
placeholder: "请输入文章内容",
|
||||
modules: {
|
||||
toolbar: {
|
||||
container: toolbarOptions,
|
||||
// container: "#toolbar",
|
||||
handlers: {
|
||||
image: function(value) {
|
||||
if (value) {
|
||||
// 触发input框选择图片文件
|
||||
document.querySelector(".avatar-uploader input").click();
|
||||
} else {
|
||||
this.quill.format("image", false);
|
||||
}
|
||||
},
|
||||
link: function(value) {
|
||||
if (value) {
|
||||
const href = prompt('注意!只支持公众号图文链接');
|
||||
this.quill.format("link", href);
|
||||
} else {
|
||||
this.quill.format("link", false);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actionUrl: process.env.VUE_APP_BASE_API +'/admin-api/mp/material/upload-news-image', // 这里写你要上传的图片服务器地址
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
uploadData: {
|
||||
"type": 'image', // TODO 芋艿:试试要不要换成 thumb
|
||||
"accountId": this.accountId,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onEditorChange(editor) {
|
||||
//内容改变事件
|
||||
this.$emit("input", this.content)
|
||||
},
|
||||
|
||||
// 富文本图片上传前
|
||||
beforeUpload() {
|
||||
// 显示 loading 动画
|
||||
this.quillUpdateImg = true
|
||||
},
|
||||
|
||||
// 图片上传成功
|
||||
// 注意!由于微信公众号的图片有访问限制,所以会显示“此图片来自微信公众号,未经允许不可引用”
|
||||
uploadSuccess(res, file) {
|
||||
// res为图片服务器返回的数据
|
||||
// 获取富文本组件实例
|
||||
let quill = this.$refs.myQuillEditor.quill
|
||||
// 如果上传成功
|
||||
const link = res.data
|
||||
if (link){
|
||||
// 获取光标所在位置
|
||||
let length = quill.getSelection().index;
|
||||
// 插入图片 res.info为服务器返回的图片地址
|
||||
quill.insertEmbed(length, 'image', link)
|
||||
// 调整光标到最后
|
||||
quill.setSelection(length + 1)
|
||||
} else {
|
||||
this.$message.error('图片插入失败')
|
||||
}
|
||||
// loading 动画消失
|
||||
this.quillUpdateImg = false;
|
||||
},
|
||||
// 富文本图片上传失败
|
||||
uploadError() {
|
||||
// loading 动画消失
|
||||
this.quillUpdateImg = false;
|
||||
this.$message.error("图片插入失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.editor {
|
||||
line-height: normal !important;
|
||||
height: 500px;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode=link]::before {
|
||||
content: "请输入链接地址:";
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||
border-right: 0;
|
||||
content: '保存';
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.ql-snow .ql-tooltip[data-mode=video]::before {
|
||||
content: "请输入视频地址:";
|
||||
}
|
||||
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: '14px';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
|
||||
content: '10px';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
|
||||
content: '18px';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
|
||||
content: '32px';
|
||||
}
|
||||
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||
content: '文本';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
content: '标题1';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
content: '标题2';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
content: '标题3';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
content: '标题4';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
content: '标题5';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
content: '标题6';
|
||||
}
|
||||
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||
content: '标准字体';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
|
||||
content: '衬线字体';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
|
||||
content: '等宽字体';
|
||||
}
|
||||
</style>
|
||||
|
||||
36
yudao-ui-admin/src/views/mp/components/wx-location/main.vue
Normal file
36
yudao-ui-admin/src/views/mp/components/wx-location/main.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<!--
|
||||
【微信消息 - 定位】
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<el-link type="primary" target="_blank" :href="'https://map.qq.com/?type=marker&isopeninfowin=1&markertype=1&pointx=' + locationY + '&pointy=' + locationX + '&name=' + label + '&ref=yudao'">
|
||||
<img :src="'https://apis.map.qq.com/ws/staticmap/v2/?zoom=10&markers=color:blue|label:A|' + locationX + ',' + locationY + '&key=' + qqMapKey + '&size=250*180'">
|
||||
<p/><i class="el-icon-map-location"></i>{{label}}
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "wxLocation",
|
||||
props: {
|
||||
locationX: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
locationY: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
label: { // 地名
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
qqMapKey: { // QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E' // 需要自定义
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,247 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
① 移除 avue 组件,使用 ElementUI 原生组件
|
||||
-->
|
||||
<template>
|
||||
<!-- 类型:图片 -->
|
||||
<div v-if="objData.type === 'image'">
|
||||
<div class="waterfall" v-loading="loading">
|
||||
<div class="waterfall-item" v-for="item in list" :key="item.mediaId">
|
||||
<img class="material-img" :src="item.url">
|
||||
<p class="item-name">{{item.name}}</p>
|
||||
<el-row class="ope-row">
|
||||
<el-button size="mini" type="success" @click="selectMaterial(item)">选择
|
||||
<i class="el-icon-circle-check el-icon--right"></i>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getMaterialPage"/>
|
||||
</div>
|
||||
<!-- 类型:语音 -->
|
||||
<div v-else-if="objData.type === 'voice'">
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="语音" align="center">
|
||||
<template v-slot="scope">
|
||||
<wx-voice-player :url="scope.row.url" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-circle-plus"
|
||||
@click="selectMaterial(scope.row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getPage"/>
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'video'">
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="标题" align="center" prop="title" />
|
||||
<el-table-column label="介绍" align="center" prop="introduction" />
|
||||
<el-table-column label="视频" align="center">
|
||||
<template v-slot="scope">
|
||||
<wx-video-player :url="scope.row.url" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-circle-plus"
|
||||
@click="selectMaterial(scope.row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getMaterialPage"/>
|
||||
</div>
|
||||
<div v-else-if="objData.type === 'news'">
|
||||
<div class="waterfall" v-loading="loading">
|
||||
<div class="waterfall-item" v-for="item in list" :key="item.mediaId" v-if="item.content && item.content.newsItem">
|
||||
<wx-news :articles="item.content.newsItem" />
|
||||
<el-row class="ope-row">
|
||||
<el-button size="mini" type="success" @click="selectMaterial(item)">
|
||||
选择<i class="el-icon-circle-check el-icon--right"></i>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getMaterialPage"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue';
|
||||
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
|
||||
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
|
||||
import { getMaterialPage } from "@/api/mp/material";
|
||||
import { getFreePublishPage } from "@/api/mp/freePublish";
|
||||
import { getDraftPage } from "@/api/mp/draft";
|
||||
|
||||
export default {
|
||||
name: "wxMaterialSelect",
|
||||
components: {
|
||||
WxNews,
|
||||
WxVoicePlayer,
|
||||
WxVideoPlayer
|
||||
},
|
||||
props: {
|
||||
objData: {
|
||||
type: Object, // type - 类型;accountId - 公众号账号编号
|
||||
required: true
|
||||
},
|
||||
newsType:{ // 图文类型:1、已发布图文;2、草稿箱图文
|
||||
type: String,
|
||||
default: "1"
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 数据列表
|
||||
list: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
accountId: this.objData.accountId,
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getPage()
|
||||
},
|
||||
methods:{
|
||||
selectMaterial(item) {
|
||||
this.$emit('selectMaterial', item)
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1
|
||||
this.getPage()
|
||||
},
|
||||
getPage() {
|
||||
this.loading = true
|
||||
if (this.objData.type === 'news' && this.newsType === '1') { // 【图文】+ 【已发布】
|
||||
this.getFreePublishPage();
|
||||
} else if (this.objData.type === 'news' && this.newsType === '2') { // 【图文】+ 【草稿】
|
||||
this.getDraftPage();
|
||||
} else { // 【素材】
|
||||
this.getMaterialPage();
|
||||
}
|
||||
},
|
||||
getMaterialPage() {
|
||||
getMaterialPage({
|
||||
...this.queryParams,
|
||||
type: this.objData.type
|
||||
}).then(response => {
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
getFreePublishPage() {
|
||||
getFreePublishPage(this.queryParams).then(response => {
|
||||
// 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面
|
||||
response.data.list.forEach(item => {
|
||||
const newsItem = item.content.newsItem;
|
||||
newsItem.forEach(article => {
|
||||
article.picUrl = article.thumbUrl;
|
||||
})
|
||||
})
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
getDraftPage() {
|
||||
getDraftPage((this.queryParams)).then(response => {
|
||||
// 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面
|
||||
response.data.list.forEach(item => {
|
||||
const newsItem = item.content.newsItem;
|
||||
newsItem.forEach(article => {
|
||||
article.picUrl = article.thumbUrl;
|
||||
})
|
||||
})
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap:10px;
|
||||
column-count: 5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
line-height: 30px;
|
||||
}
|
||||
@media (min-width: 992px) and (max-width: 1300px) {
|
||||
.waterfall {
|
||||
column-count: 3;
|
||||
}
|
||||
p {
|
||||
color:red;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
.waterfall {
|
||||
column-count: 2;
|
||||
}
|
||||
p {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
/*瀑布流样式*/
|
||||
</style>
|
||||
101
yudao-ui-admin/src/views/mp/components/wx-msg/card.scss
Normal file
101
yudao-ui-admin/src/views/mp/components/wx-msg/card.scss
Normal file
@@ -0,0 +1,101 @@
|
||||
.avue-card{
|
||||
&__item{
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #e8e8e8;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
color: rgba(0,0,0,.65);
|
||||
font-size: 14px;
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5;
|
||||
list-style: none;
|
||||
font-feature-settings: "tnum";
|
||||
cursor: pointer;
|
||||
height:200px;
|
||||
&:hover{
|
||||
border-color: rgba(0,0,0,.09);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,.09);
|
||||
}
|
||||
&--add{
|
||||
border:1px dashed #000;
|
||||
width: 100%;
|
||||
color: rgba(0,0,0,.45);
|
||||
background-color: #fff;
|
||||
border-color: #d9d9d9;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
i{
|
||||
margin-right: 10px;
|
||||
}
|
||||
&:hover{
|
||||
color: #40a9ff;
|
||||
background-color: #fff;
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__body{
|
||||
display: flex;
|
||||
padding: 24px;
|
||||
}
|
||||
&__detail{
|
||||
flex:1
|
||||
}
|
||||
&__avatar{
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 48px;
|
||||
overflow: hidden;
|
||||
margin-right: 12px;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
&__title{
|
||||
color: rgba(0,0,0,.85);
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
&:hover{
|
||||
color:#1890ff;
|
||||
}
|
||||
}
|
||||
&__info{
|
||||
color: rgba(0,0,0,.45);
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
height: 64px;
|
||||
}
|
||||
&__menu{
|
||||
display: flex;
|
||||
justify-content:space-around;
|
||||
height: 50px;
|
||||
background: #f7f9fa;
|
||||
color: rgba(0,0,0,.45);
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
&:hover{
|
||||
color:#1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** joolun 额外加的 */
|
||||
.avue-comment__main {
|
||||
flex: unset!important;
|
||||
border-radius: 5px!important;
|
||||
margin: 0 8px!important;
|
||||
}
|
||||
.avue-comment__header {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
.avue-comment__body {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
88
yudao-ui-admin/src/views/mp/components/wx-msg/comment.scss
Normal file
88
yudao-ui-admin/src/views/mp/components/wx-msg/comment.scss
Normal file
@@ -0,0 +1,88 @@
|
||||
/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */
|
||||
.avue-comment{
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
&--reverse{
|
||||
flex-direction:row-reverse;
|
||||
.avue-comment__main{
|
||||
&:before,&:after{
|
||||
left: auto;
|
||||
right: -8px;
|
||||
border-width: 8px 0 8px 8px;
|
||||
}
|
||||
&:before{
|
||||
border-left-color: #dedede;
|
||||
}
|
||||
&:after{
|
||||
border-left-color: #f8f8f8;
|
||||
margin-right: 1px;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__avatar{
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&__header{
|
||||
padding: 5px 15px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
&__author{
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
&__main{
|
||||
flex:1;
|
||||
margin: 0 20px;
|
||||
position: relative;
|
||||
border: 1px solid #dedede;
|
||||
border-radius: 2px;
|
||||
&:before,&:after{
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
right: 100%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
display: block;
|
||||
content: " ";
|
||||
border-color: transparent;
|
||||
border-style: solid solid outset;
|
||||
border-width: 8px 8px 8px 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
&:before {
|
||||
border-right-color: #dedede;
|
||||
z-index: 1;
|
||||
}
|
||||
&:after{
|
||||
border-right-color: #f8f8f8;
|
||||
margin-left: 1px;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
&__body{
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
font-family: Segoe UI,Lucida Grande,Helvetica,Arial,Microsoft YaHei,FreeSans,Arimo,Droid Sans,wenquanyi micro hei,Hiragino Sans GB,Hiragino Sans GB W3,FontAwesome,sans-serif;color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
blockquote{
|
||||
margin:0;
|
||||
font-family: Georgia,Times New Roman,Times,Kai,Kaiti SC,KaiTi,BiauKai,FontAwesome,serif;
|
||||
padding: 1px 0 1px 15px;
|
||||
border-left: 4px solid #ddd;
|
||||
}
|
||||
}
|
||||
294
yudao-ui-admin/src/views/mp/components/wx-msg/main.vue
Normal file
294
yudao-ui-admin/src/views/mp/components/wx-msg/main.vue
Normal file
@@ -0,0 +1,294 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
① 移除暂时用不到的 websocket
|
||||
② 代码优化,补充注释,提升阅读性
|
||||
-->
|
||||
<template>
|
||||
<div class="msg-main">
|
||||
<div class="msg-div" :id="'msg-div' + nowStr">
|
||||
<!-- 加载更多 -->
|
||||
<div v-loading="loading"></div>
|
||||
<div v-if="!loading">
|
||||
<div class="el-table__empty-block" v-if="loadMore" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span></div>
|
||||
<div class="el-table__empty-block" v-if="!loadMore"><span class="el-table__empty-text">没有更多了</span></div>
|
||||
</div>
|
||||
<!-- 消息列表 -->
|
||||
<div class="execution" v-for="item in list" :key='item.id'>
|
||||
<div class="avue-comment" :class="item.sendFrom === 2 ? 'avue-comment--reverse' : ''">
|
||||
<div class="avatar-div">
|
||||
<img :src="item.sendFrom === 1 ? user.avatar : mp.avatar" class="avue-comment__avatar">
|
||||
<div class="avue-comment__author">{{item.sendFrom === 1 ? user.nickname : mp.nickname }}</div>
|
||||
</div>
|
||||
<div class="avue-comment__main">
|
||||
<div class="avue-comment__header">
|
||||
<div class="avue-comment__create_time">{{ parseTime(item.createTime) }}</div>
|
||||
</div>
|
||||
<div class="avue-comment__body" :style="item.sendFrom === 2 ? 'background: #6BED72;' : ''">
|
||||
<!-- 【事件】区域 -->
|
||||
<div v-if="item.type === 'event' && item.event === 'subscribe'">
|
||||
<el-tag type="success" size="mini">关注</el-tag>
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'unsubscribe'">
|
||||
<el-tag type="danger" size="mini">取消关注</el-tag>
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'CLICK'">
|
||||
<el-tag size="mini">点击菜单</el-tag>【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'VIEW'">
|
||||
<el-tag size="mini">点击菜单链接</el-tag>【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'scancode_waitmsg'">
|
||||
<el-tag size="mini">扫码结果</el-tag>【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'scancode_push'">
|
||||
<el-tag size="mini">扫码结果</el-tag>【{{ item.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'pic_sysphoto'">
|
||||
<el-tag size="mini">系统拍照发图</el-tag>
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'pic_photo_or_album'">
|
||||
<el-tag size="mini">拍照或者相册</el-tag>
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'pic_weixin'">
|
||||
<el-tag size="mini">微信相册</el-tag>
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event' && item.event === 'location_select'">
|
||||
<el-tag size="mini">选择地理位置</el-tag>
|
||||
</div>
|
||||
<div v-else-if="item.type === 'event'">
|
||||
<el-tag type="danger" size="mini">未知事件类型</el-tag>
|
||||
</div>
|
||||
<!-- 【消息】区域 -->
|
||||
<div v-else-if="item.type === 'text'">{{ item.content }}</div>
|
||||
<div v-else-if="item.type === 'voice'">
|
||||
<wx-voice-player :url="item.mediaUrl" :content="item.recognition" />
|
||||
</div>
|
||||
<div v-else-if="item.type === 'image'">
|
||||
<a target="_blank" :href="item.mediaUrl">
|
||||
<img :src="item.mediaUrl" style="width: 100px">
|
||||
</a>
|
||||
</div>
|
||||
<div v-else-if="item.type === 'video' || item.type === 'shortvideo'" style="text-align: center">
|
||||
<wx-video-player :url="item.mediaUrl" />
|
||||
</div>
|
||||
<div v-else-if="item.type === 'link'" class="avue-card__detail">
|
||||
<el-link type="success" :underline="false" target="_blank" :href="item.url">
|
||||
<div class="avue-card__title"><i class="el-icon-link"></i>{{ item.title }}</div>
|
||||
</el-link>
|
||||
<div class="avue-card__info" style="height: unset">{{item.description}}</div>
|
||||
</div>
|
||||
<!-- TODO 芋艿:待完善 -->
|
||||
<div v-else-if="item.type === 'location'">
|
||||
<wx-location :label="item.label" :location-y="item.locationY" :location-x="item.locationX" />
|
||||
</div>
|
||||
<div v-else-if="item.type === 'news'" style="width: 300px"> <!-- TODO 芋艿:待测试;详情页也存在类似的情况 -->
|
||||
<wx-news :articles="item.articles" />
|
||||
</div>
|
||||
<div v-else-if="item.type === 'music'">
|
||||
<wx-music :title="item.title" :description="item.description" :thumb-media-url="item.thumbMediaUrl"
|
||||
:music-url="item.musicUrl" :hq-music-url="item.hqMusicUrl" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="msg-send" v-loading="sendLoading">
|
||||
<wx-reply-select ref="replySelect" :objData="objData" />
|
||||
<el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMessagePage, sendMessage } from '@/api/mp/message'
|
||||
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
|
||||
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
|
||||
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue';
|
||||
import WxLocation from '@/views/mp/components/wx-location/main.vue';
|
||||
import WxMusic from '@/views/mp/components/wx-music/main.vue';
|
||||
import { getUser } from "@/api/mp/user";
|
||||
|
||||
export default {
|
||||
name: "wxMsg",
|
||||
components: {
|
||||
WxReplySelect,
|
||||
WxVideoPlayer,
|
||||
WxVoicePlayer,
|
||||
WxNews,
|
||||
WxLocation,
|
||||
WxMusic
|
||||
},
|
||||
props: {
|
||||
userId: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
|
||||
loading: false, // 消息列表是否正在加载中
|
||||
loadMore: true, // 是否可以加载更多
|
||||
list: [], // 消息列表
|
||||
queryParams: {
|
||||
pageNo: 1, // 当前页数
|
||||
pageSize: 14, // 每页显示多少条
|
||||
accountId: undefined,
|
||||
},
|
||||
user: { // 由于微信不再提供昵称,直接使用“用户”展示
|
||||
nickname: '用户',
|
||||
avatar: require("@/assets/images/profile.jpg"),
|
||||
accountId: 0, // 公众号账号编号
|
||||
},
|
||||
mp: {
|
||||
nickname: '公众号',
|
||||
avatar: require("@/assets/images/wechat.png"),
|
||||
},
|
||||
|
||||
// ========= 消息发送 =========
|
||||
sendLoading: false, // 发送消息是否加载中
|
||||
objData: { // 微信发送消息
|
||||
type: 'text',
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 获得用户信息
|
||||
getUser(this.userId).then(response => {
|
||||
this.user.nickname = response.data.nickname && response.data.nickname.length > 0 ?
|
||||
response.data.nickname : this.user.nickname;
|
||||
this.user.avatar = response.data.avatar && this.user.avatar.length > 0 ?
|
||||
response.data.avatar : this.user.avatar;
|
||||
this.user.accountId = response.data.accountId;
|
||||
// 设置公众号账号编号
|
||||
this.queryParams.accountId = response.data.accountId;
|
||||
this.objData.accountId = response.data.accountId;
|
||||
|
||||
// 加载消息
|
||||
console.log(this.queryParams)
|
||||
this.refreshChange()
|
||||
})
|
||||
},
|
||||
methods:{
|
||||
sendMsg(){
|
||||
if (!this.objData) {
|
||||
return;
|
||||
}
|
||||
// 公众号限制:客服消息,公众号只允许发送一条
|
||||
if (this.objData.type === 'news'
|
||||
&& this.objData.articles.length > 1) {
|
||||
this.objData.articles = [this.objData.articles[0]]
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '图文消息条数限制在 1 条以内,已默认发送第一条',
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
|
||||
// 执行发送
|
||||
this.sendLoading = true
|
||||
sendMessage(Object.assign({
|
||||
userId: this.userId
|
||||
}, {
|
||||
...this.objData
|
||||
})).then(response => {
|
||||
this.sendLoading = false
|
||||
// 添加到消息列表,并滚动
|
||||
this.list = [...this.list , ...[response.data] ]
|
||||
this.scrollToBottom()
|
||||
// 重置 objData 状态
|
||||
this.$refs['replySelect'].deleteObj(); // 重置,避免 tab 的数据未清理
|
||||
}).catch(() => {
|
||||
this.sendLoading = false
|
||||
})
|
||||
},
|
||||
loadingMore() {
|
||||
this.queryParams.pageNo++
|
||||
this.getPage(this.queryParams)
|
||||
},
|
||||
getPage(page, params) {
|
||||
this.loading = true
|
||||
getMessagePage(Object.assign({
|
||||
pageNo: page.pageNo,
|
||||
pageSize: page.pageSize,
|
||||
userId: this.userId,
|
||||
accountId: page.accountId,
|
||||
}, params)).then(response => {
|
||||
// 计算当前的滚动高度
|
||||
const msgDiv = document.getElementById('msg-div' + this.nowStr);
|
||||
let scrollHeight = 0
|
||||
if(msgDiv){
|
||||
scrollHeight = msgDiv.scrollHeight
|
||||
}
|
||||
|
||||
// 处理数据
|
||||
const data = response.data.list.reverse();
|
||||
this.list = [...data, ...this.list]
|
||||
this.loading = false
|
||||
if (data.length < this.queryParams.pageSize || data.length === 0){
|
||||
this.loadMore = false
|
||||
}
|
||||
this.queryParams.pageNo = page.pageNo
|
||||
this.queryParams.pageSize = page.pageSize
|
||||
|
||||
// 滚动到原来的位置
|
||||
if(this.queryParams.pageNo === 1) { // 定位到消息底部
|
||||
this.scrollToBottom()
|
||||
} else if (data.length !== 0) { // 定位滚动条
|
||||
this.$nextTick(() => {
|
||||
if (scrollHeight !== 0){
|
||||
msgDiv.scrollTop = document.getElementById('msg-div'+this.nowStr).scrollHeight - scrollHeight - 100
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 刷新回调
|
||||
*/
|
||||
refreshChange() {
|
||||
this.getPage(this.queryParams)
|
||||
},
|
||||
/** 定位到消息底部 */
|
||||
scrollToBottom: function () {
|
||||
this.$nextTick(() => {
|
||||
let div = document.getElementById('msg-div' + this.nowStr)
|
||||
div.scrollTop = div.scrollHeight
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
/* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc */
|
||||
@import './comment.scss';
|
||||
@import './card.scss';
|
||||
|
||||
.msg-main {
|
||||
margin-top: -30px;
|
||||
padding: 10px;
|
||||
}
|
||||
.msg-div {
|
||||
height: 50vh;
|
||||
overflow: auto;
|
||||
background-color: #eaeaea;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.msg-send {
|
||||
padding: 10px;
|
||||
}
|
||||
.avatar-div {
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
}
|
||||
.send-but {
|
||||
float: right;
|
||||
margin-top: 8px!important;
|
||||
}
|
||||
</style>
|
||||
|
||||
52
yudao-ui-admin/src/views/mp/components/wx-music/main.vue
Normal file
52
yudao-ui-admin/src/views/mp/components/wx-music/main.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<!--
|
||||
【微信消息 - 音乐】
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<el-link type="success" :underline="false" target="_blank" :href="hqMusicUrl ? hqMusicUrl : musicUrl">
|
||||
<div class="avue-card__body" style="padding:10px;background-color: #fff;border-radius: 5px">
|
||||
<div class="avue-card__avatar">
|
||||
<img :src="thumbMediaUrl" alt=""/>
|
||||
</div>
|
||||
<div class="avue-card__detail">
|
||||
<div class="avue-card__title" style="margin-bottom:unset">{{ title }}</div>
|
||||
<div class="avue-card__info" style="height: unset">{{ description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "wxMusic",
|
||||
props: {
|
||||
title: {
|
||||
required: false,
|
||||
type: String
|
||||
},
|
||||
description: {
|
||||
required: false,
|
||||
type: String
|
||||
},
|
||||
musicUrl: {
|
||||
required: false,
|
||||
type: String
|
||||
},
|
||||
hqMusicUrl: {
|
||||
required: false,
|
||||
type: String
|
||||
},
|
||||
thumbMediaUrl: {
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 因为 joolun 实现依赖 avue 组件,该页面使用了 card.scc */
|
||||
@import '../wx-msg/card.scss';
|
||||
</style>
|
||||
104
yudao-ui-admin/src/views/mp/components/wx-news/main.vue
Normal file
104
yudao-ui-admin/src/views/mp/components/wx-news/main.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
【微信消息 - 图文】
|
||||
芋道源码:
|
||||
① 代码优化,补充注释,提升阅读性
|
||||
-->
|
||||
<template>
|
||||
<div class="news-home">
|
||||
<div v-for="(article, index) in articles" :key="index" class="news-div">
|
||||
<!-- 头条 -->
|
||||
<a target="_blank" :href="article.url" v-if="index === 0">
|
||||
<div class="news-main">
|
||||
<div class="news-content">
|
||||
<img class="material-img" :src="article.picUrl" width="280px" height="120px"/>
|
||||
<div class="news-content-title">
|
||||
<span>{{article.title}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<!-- 二条/三条等等 -->
|
||||
<a target="_blank" :href="article.url" v-else>
|
||||
<div class="news-main-item">
|
||||
<div class="news-content-item">
|
||||
<div class="news-content-item-title">{{article.title}}</div>
|
||||
<div class="news-content-item-img">
|
||||
<img class="material-img" :src="article.picUrl" height="100%"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "wxNews",
|
||||
props: {
|
||||
articles: {
|
||||
type: Array // title - 标题;description - 描述;picUrl - 图片连接;url - 跳转链接
|
||||
}
|
||||
},
|
||||
// created() {
|
||||
// console.log(this.articles)
|
||||
// },
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-home {
|
||||
background-color: #FFFFFF;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.news-main {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.news-content {
|
||||
background-color: #acadae;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.news-content-title {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-color: black;
|
||||
width: 98%;
|
||||
padding: 1%;
|
||||
opacity: 0.65;
|
||||
white-space: normal;
|
||||
box-sizing: unset!important
|
||||
}
|
||||
.news-main-item {
|
||||
background-color: #FFFFFF;
|
||||
padding: 5px 0;
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
.news-content-item {
|
||||
position: relative;
|
||||
}
|
||||
.news-content-item-title {
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
width: 70%;
|
||||
margin-left: 1%;
|
||||
white-space: normal
|
||||
}
|
||||
.news-content-item-img {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
background-color: #acadae;
|
||||
margin-right: 1%;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
547
yudao-ui-admin/src/views/mp/components/wx-reply/main.vue
Normal file
547
yudao-ui-admin/src/views/mp/components/wx-reply/main.vue
Normal file
@@ -0,0 +1,547 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
① 移除多余的 rep 为前缀的变量,让 message 消息更简单
|
||||
② 代码优化,补充注释,提升阅读性
|
||||
③ 优化消息的临时缓存策略,发送消息时,只清理被发送消息的 tab,不会强制切回到 text 输入
|
||||
④ 支持发送【视频】消息时,支持新建视频
|
||||
-->
|
||||
<template>
|
||||
<el-tabs type="border-card" v-model="objData.type" @tab-click="handleClick">
|
||||
<!-- 类型 1:文本 -->
|
||||
<el-tab-pane name="text">
|
||||
<span slot="label"><i class="el-icon-document"></i> 文本</span>
|
||||
<el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="objData.content" @input="inputContent" />
|
||||
</el-tab-pane>
|
||||
<!-- 类型 2:图片 -->
|
||||
<el-tab-pane name="image">
|
||||
<span slot="label"><i class="el-icon-picture"></i> 图片</span>
|
||||
<el-row>
|
||||
<!-- 情况一:已经选择好素材、或者上传好图片 -->
|
||||
<div class="select-item" v-if="objData.url">
|
||||
<img class="material-img" :src="objData.url">
|
||||
<p class="item-name" v-if="objData.name">{{objData.name}}</p>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- 情况二:未做完上述操作 -->
|
||||
<div v-else>
|
||||
<el-row style="text-align: center">
|
||||
<!-- 选择素材 -->
|
||||
<el-col :span="12" class="col-select">
|
||||
<el-button type="success" @click="openMaterial">
|
||||
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
|
||||
</el-button>
|
||||
<el-dialog title="选择图片" :visible.sync="dialogImageVisible" width="90%" append-to-body>
|
||||
<wx-material-select :obj-data="objData" @selectMaterial="selectMaterial" />
|
||||
</el-dialog>
|
||||
</el-col>
|
||||
<!-- 文件上传 -->
|
||||
<el-col :span="12" class="col-add">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:before-upload="beforeImageUpload" :on-success="handleUploadSuccess">
|
||||
<el-button type="primary">上传图片</el-button>
|
||||
<div slot="tip" class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<!-- 类型 3:语音 -->
|
||||
<el-tab-pane name="voice">
|
||||
<span slot="label"><i class="el-icon-phone"></i> 语音</span>
|
||||
<el-row>
|
||||
<div class="select-item2" v-if="objData.url">
|
||||
<p class="item-name">{{objData.name}}</p>
|
||||
<div class="item-infos">
|
||||
<wx-voice-player :url="objData.url" />
|
||||
</div>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-row style="text-align: center">
|
||||
<!-- 选择素材 -->
|
||||
<el-col :span="12" class="col-select">
|
||||
<el-button type="success" @click="openMaterial">
|
||||
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
|
||||
</el-button>
|
||||
<el-dialog title="选择语音" :visible.sync="dialogVoiceVisible" width="90%" append-to-body>
|
||||
<WxMaterialSelect :objData="objData" @selectMaterial="selectMaterial"></WxMaterialSelect>
|
||||
</el-dialog>
|
||||
</el-col>
|
||||
<!-- 文件上传 -->
|
||||
<el-col :span="12" class="col-add">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:before-upload="beforeVoiceUpload" :on-success="handleUploadSuccess">
|
||||
<el-button type="primary">点击上传</el-button>
|
||||
<div slot="tip" class="el-upload__tip">格式支持 mp3/wma/wav/amr,文件大小不超过 2M,播放长度不超过 60s</div>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<!-- 类型 4:视频 -->
|
||||
<el-tab-pane name="video">
|
||||
<span slot="label"><i class="el-icon-share"></i> 视频</span>
|
||||
<el-row>
|
||||
<el-input v-model="objData.title" placeholder="请输入标题" @input="inputContent" />
|
||||
<div style="margin: 20px 0;"></div>
|
||||
<el-input v-model="objData.description" placeholder="请输入描述" @input="inputContent" />
|
||||
<div style="margin: 20px 0;"></div>
|
||||
<div style="text-align: center;">
|
||||
<wx-video-player v-if="objData.url" :url="objData.url" />
|
||||
</div>
|
||||
<div style="margin: 20px 0;"></div>
|
||||
<el-row style="text-align: center">
|
||||
<!-- 选择素材 -->
|
||||
<el-col :span="12">
|
||||
<el-button type="success" @click="openMaterial">
|
||||
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
|
||||
</el-button>
|
||||
<el-dialog title="选择视频" :visible.sync="dialogVideoVisible" width="90%" append-to-body>
|
||||
<wx-material-select :objData="objData" @selectMaterial="selectMaterial" />
|
||||
</el-dialog>
|
||||
</el-col>
|
||||
<!-- 文件上传 -->
|
||||
<el-col :span="12">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:before-upload="beforeVideoUpload" :on-success="handleUploadSuccess">
|
||||
<el-button type="primary">新建视频<i class="el-icon-upload el-icon--right"></i></el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<!-- 类型 5:图文 -->
|
||||
<el-tab-pane name="news">
|
||||
<span slot="label"><i class="el-icon-news"></i> 图文</span>
|
||||
<el-row>
|
||||
<div class="select-item" v-if="objData.articles">
|
||||
<wx-news :articles="objData.articles" />
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj" />
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- 选择素材 -->
|
||||
<div v-if="!objData.content">
|
||||
<el-row style="text-align: center">
|
||||
<el-col :span="24">
|
||||
<el-button type="success" @click="openMaterial">{{newsType === '1' ? '选择已发布图文' : '选择草稿箱图文'}}<i class="el-icon-circle-check el-icon--right"></i></el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-dialog title="选择图文" :visible.sync="dialogNewsVisible" width="90%" append-to-body>
|
||||
<wx-material-select :objData="objData" @selectMaterial="selectMaterial" :newsType="newsType" />
|
||||
</el-dialog>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<!-- 类型 6:音乐 -->
|
||||
<el-tab-pane name="music">
|
||||
<span slot="label"><i class="el-icon-service"></i> 音乐</span>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<div class="thumb-div">
|
||||
<img style="width: 100px" v-if="objData.thumbMediaUrl" :src="objData.thumbMediaUrl">
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
<div class="thumb-but">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:before-upload="beforeThumbImageUpload" :on-success="handleUploadSuccess">
|
||||
<el-button slot="trigger" size="mini" type="text">本地上传</el-button>
|
||||
<el-button size="mini" type="text" @click="openMaterial" style="margin-left: 5px">素材库选择</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog title="选择图片" :visible.sync="dialogThumbVisible" width="80%" append-to-body>
|
||||
<wx-material-select :objData="{type:'image', accountId: objData.accountId}" @selectMaterial="selectMaterial" />
|
||||
</el-dialog>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-input v-model="objData.title" placeholder="请输入标题" @input="inputContent" />
|
||||
<div style="margin: 20px 0;"></div>
|
||||
<el-input v-model="objData.description" placeholder="请输入描述" @input="inputContent" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div style="margin: 20px 0;"></div>
|
||||
<el-input v-model="objData.musicUrl" placeholder="请输入音乐链接" @input="inputContent" />
|
||||
<div style="margin: 20px 0;"></div>
|
||||
<el-input v-model="objData.hqMusicUrl" placeholder="请输入高质量音乐链接" @input="inputContent" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue'
|
||||
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
|
||||
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
|
||||
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
|
||||
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
name: "wxReplySelect",
|
||||
components: {
|
||||
WxNews,
|
||||
WxMaterialSelect,
|
||||
WxVoicePlayer,
|
||||
WxVideoPlayer
|
||||
},
|
||||
props: {
|
||||
objData: { // 消息对象。
|
||||
type: Object, // 设置为 Object 的原因,方便属性的传递
|
||||
required: true,
|
||||
},
|
||||
newsType:{ // 图文类型:1、已发布图文;2、草稿箱图文
|
||||
type: String,
|
||||
default: "1"
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tempPlayerObj: {
|
||||
type: '2'
|
||||
},
|
||||
|
||||
tempObj: new Map().set( // 临时缓存,切换消息类型的 tab 的时候,可以保存对应的数据;
|
||||
this.objData.type, // 消息类型
|
||||
Object.assign({}, this.objData)), // 消息内容
|
||||
|
||||
// ========== 素材选择的弹窗,是否可见 ==========
|
||||
dialogNewsVisible: false, // 图文
|
||||
dialogImageVisible: false, // 图片
|
||||
dialogVoiceVisible: false, // 语音
|
||||
dialogVideoVisible: false, // 视频
|
||||
dialogThumbVisible: false, // 缩略图
|
||||
|
||||
// ========== 文件上传(图片、语音、视频) ==========
|
||||
fileList: [], // 文件列表
|
||||
uploadData: {
|
||||
"accountId": undefined,
|
||||
"type": this.objData.type,
|
||||
"title":'',
|
||||
"introduction":''
|
||||
},
|
||||
actionUrl: process.env.VUE_APP_BASE_API + '/admin-api/mp/material/upload-temporary',
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
beforeThumbImageUpload(file){
|
||||
const isType = file.type === 'image/jpeg'
|
||||
|| file.type === 'image/png'
|
||||
|| file.type === 'image/gif'
|
||||
|| file.type === 'image/bmp'
|
||||
|| file.type === 'image/jpg';
|
||||
if (!isType) {
|
||||
this.$message.error('上传图片格式不对!');
|
||||
return false;
|
||||
}
|
||||
const isLt = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt) {
|
||||
this.$message.error('上传图片大小不能超过 2M!');
|
||||
return false;
|
||||
}
|
||||
this.uploadData.accountId = this.objData.accountId;
|
||||
return true;
|
||||
},
|
||||
beforeVoiceUpload(file){
|
||||
// 校验格式
|
||||
const isType = file.type === 'audio/mp3'
|
||||
|| file.type === 'audio/mpeg'
|
||||
|| file.type === 'audio/wma'
|
||||
|| file.type === 'audio/wav'
|
||||
|| file.type === 'audio/amr';
|
||||
if (!isType) {
|
||||
this.$message.error('上传语音格式不对!' + file.type);
|
||||
return false;
|
||||
}
|
||||
// 校验大小
|
||||
const isLt = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt) {
|
||||
this.$message.error('上传语音大小不能超过 2M!');
|
||||
return false;
|
||||
}
|
||||
this.uploadData.accountId = this.objData.accountId;
|
||||
return true;
|
||||
},
|
||||
beforeImageUpload(file) {
|
||||
// 校验格式
|
||||
const isType = file.type === 'image/jpeg'
|
||||
|| file.type === 'image/png'
|
||||
|| file.type === 'image/gif'
|
||||
|| file.type === 'image/bmp'
|
||||
|| file.type === 'image/jpg';
|
||||
if (!isType) {
|
||||
this.$message.error('上传图片格式不对!');
|
||||
return false;
|
||||
}
|
||||
// 校验大小
|
||||
const isLt = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt) {
|
||||
this.$message.error('上传图片大小不能超过 2M!');
|
||||
return false;
|
||||
}
|
||||
this.uploadData.accountId = this.objData.accountId;
|
||||
return true;
|
||||
},
|
||||
beforeVideoUpload(file){
|
||||
// 校验格式
|
||||
const isType = file.type === 'video/mp4';
|
||||
if (!isType) {
|
||||
this.$message.error('上传视频格式不对!');
|
||||
return false;
|
||||
}
|
||||
// 校验大小
|
||||
const isLt = file.size / 1024 / 1024 < 10;
|
||||
if (!isLt) {
|
||||
this.$message.error('上传视频大小不能超过 10M!');
|
||||
return false;
|
||||
}
|
||||
this.uploadData.accountId = this.objData.accountId;
|
||||
return true;
|
||||
},
|
||||
handleUploadSuccess(response, file, fileList) {
|
||||
if (response.code !== 0) {
|
||||
this.$message.error('上传出错:' + response.msg)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清空上传时的各种数据
|
||||
this.fileList = []
|
||||
this.uploadData.title = ''
|
||||
this.uploadData.introduction = ''
|
||||
|
||||
// 上传好的文件,本质是个素材,所以可以进行选中
|
||||
let item = response.data
|
||||
this.selectMaterial(item)
|
||||
},
|
||||
/**
|
||||
* 切换消息类型的 tab
|
||||
*
|
||||
* @param tab tab
|
||||
*/
|
||||
handleClick(tab) {
|
||||
// 设置后续文件上传的文件类型
|
||||
this.uploadData.type = this.objData.type;
|
||||
if (this.uploadData.type === 'music') { // 【音乐】上传的是缩略图
|
||||
this.uploadData.type = 'thumb';
|
||||
}
|
||||
|
||||
// 从 tempObj 临时缓存中,获取对应的数据,并设置回 objData
|
||||
let tempObjItem = this.tempObj.get(this.objData.type)
|
||||
if (tempObjItem) {
|
||||
this.objData.content = tempObjItem.content ? tempObjItem.content : null
|
||||
this.objData.mediaId = tempObjItem.mediaId ? tempObjItem.mediaId : null
|
||||
this.objData.url = tempObjItem.url ? tempObjItem.url : null
|
||||
this.objData.name = tempObjItem.url ? tempObjItem.name : null
|
||||
this.objData.title = tempObjItem.title ? tempObjItem.title : null
|
||||
this.objData.description = tempObjItem.description ? tempObjItem.description : null
|
||||
return;
|
||||
}
|
||||
// 如果获取不到,需要把 objData 复原
|
||||
// 必须使用 $set 赋值,不然 input 无法输入内容
|
||||
this.$set(this.objData, 'content', '');
|
||||
this.$delete(this.objData, 'mediaId');
|
||||
this.$delete(this.objData, 'url');
|
||||
this.$set(this.objData, 'title', '');
|
||||
this.$set(this.objData, 'description', '');
|
||||
|
||||
},
|
||||
/**
|
||||
* 选择素材,将设置设置到 objData 变量
|
||||
*
|
||||
* @param item 素材
|
||||
*/
|
||||
selectMaterial(item) {
|
||||
// 选择好素材,所以隐藏弹窗
|
||||
this.closeMaterial();
|
||||
|
||||
// 创建 tempObjItem 对象,并设置对应的值
|
||||
let tempObjItem = {}
|
||||
tempObjItem.type = this.objData.type;
|
||||
if (this.objData.type === 'news') {
|
||||
tempObjItem.articles = item.content.newsItem
|
||||
this.objData.articles = item.content.newsItem
|
||||
} else if (this.objData.type === 'music') { // 音乐需要特殊处理,因为选择的是图片的缩略图
|
||||
tempObjItem.thumbMediaId = item.mediaId
|
||||
this.objData.thumbMediaId = item.mediaId
|
||||
tempObjItem.thumbMediaUrl = item.url
|
||||
this.objData.thumbMediaUrl = item.url
|
||||
// title、introduction、musicUrl、hqMusicUrl:从 objData 到 tempObjItem,避免上传素材后,被覆盖掉
|
||||
tempObjItem.title = this.objData.title || ''
|
||||
tempObjItem.introduction = this.objData.introduction || ''
|
||||
tempObjItem.musicUrl = this.objData.musicUrl || ''
|
||||
tempObjItem.hqMusicUrl = this.objData.hqMusicUrl || ''
|
||||
} else if (this.objData.type === 'image'
|
||||
|| this.objData.type === 'voice') {
|
||||
tempObjItem.mediaId = item.mediaId
|
||||
this.objData.mediaId = item.mediaId
|
||||
tempObjItem.url = item.url;
|
||||
this.objData.url = item.url;
|
||||
tempObjItem.name = item.name
|
||||
this.objData.name = item.name
|
||||
} else if (this.objData.type === 'video') {
|
||||
tempObjItem.mediaId = item.mediaId
|
||||
this.objData.mediaId = item.mediaId
|
||||
tempObjItem.url = item.url;
|
||||
this.objData.url = item.url;
|
||||
tempObjItem.name = item.name
|
||||
this.objData.name = item.name
|
||||
// title、introduction:从 item 到 tempObjItem,因为素材里有 title、introduction
|
||||
if (item.title) {
|
||||
this.objData.title = item.title || ''
|
||||
tempObjItem.title = item.title || ''
|
||||
}
|
||||
if (item.introduction) {
|
||||
this.objData.description = item.introduction || '' // 消息使用的是 description,素材使用的是 introduction,所以转换下
|
||||
tempObjItem.description = item.introduction || ''
|
||||
}
|
||||
} else if (this.objData.type === 'text') {
|
||||
this.objData.content = item.content || ''
|
||||
}
|
||||
// 最终设置到临时缓存
|
||||
this.tempObj.set(this.objData.type, tempObjItem)
|
||||
},
|
||||
openMaterial() {
|
||||
if (this.objData.type === 'news') {
|
||||
this.dialogNewsVisible = true
|
||||
} else if(this.objData.type === 'image') {
|
||||
this.dialogImageVisible = true
|
||||
} else if(this.objData.type === 'voice') {
|
||||
this.dialogVoiceVisible = true
|
||||
} else if(this.objData.type === 'video') {
|
||||
this.dialogVideoVisible = true
|
||||
} else if(this.objData.type === 'music') {
|
||||
this.dialogThumbVisible = true
|
||||
}
|
||||
},
|
||||
closeMaterial() {
|
||||
this.dialogNewsVisible = false
|
||||
this.dialogImageVisible = false
|
||||
this.dialogVoiceVisible = false
|
||||
this.dialogVideoVisible = false
|
||||
this.dialogThumbVisible = false
|
||||
},
|
||||
deleteObj() {
|
||||
if (this.objData.type === 'news') {
|
||||
this.$delete(this.objData, 'articles');
|
||||
} else if(this.objData.type === 'image') {
|
||||
this.objData.mediaId = null
|
||||
this.$delete(this.objData, 'url');
|
||||
this.objData.name = null
|
||||
} else if(this.objData.type === 'voice') {
|
||||
this.objData.mediaId = null
|
||||
this.$delete(this.objData, 'url');
|
||||
this.objData.name = null
|
||||
} else if(this.objData.type === 'video') {
|
||||
this.objData.mediaId = null
|
||||
this.$delete(this.objData, 'url');
|
||||
this.objData.name = null
|
||||
this.objData.title = null
|
||||
this.objData.description = null
|
||||
} else if(this.objData.type === 'music') {
|
||||
this.objData.thumbMediaId = null
|
||||
this.objData.thumbMediaUrl = null
|
||||
this.objData.title = null
|
||||
this.objData.description = null
|
||||
this.objData.musicUrl = null
|
||||
this.objData.hqMusicUrl = null
|
||||
} else if(this.objData.type === 'text') {
|
||||
this.objData.content = null
|
||||
}
|
||||
// 覆盖缓存
|
||||
this.tempObj.set(this.objData.type, Object.assign({}, this.objData));
|
||||
},
|
||||
/**
|
||||
* 输入时,缓存每次 objData 到 tempObj 中
|
||||
*
|
||||
* why?不确定为什么 v-model="objData.content" 不能自动缓存,所以通过这样的方式
|
||||
*/
|
||||
inputContent(str) {
|
||||
// 覆盖缓存
|
||||
this.tempObj.set(this.objData.type, Object.assign({}, this.objData));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.public-account-management{
|
||||
.el-input{
|
||||
width: 70%;
|
||||
margin-right: 2%;
|
||||
}
|
||||
}
|
||||
.pagination{
|
||||
text-align: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
.select-item{
|
||||
width: 280px;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.select-item2{
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.ope-row{
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.item-name{
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
.el-form-item__content{
|
||||
line-height:unset!important;
|
||||
}
|
||||
.col-select{
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
width: 49.5%;
|
||||
}
|
||||
.col-select2{
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
}
|
||||
.col-add{
|
||||
border: 1px solid rgb(234, 234, 234);
|
||||
padding: 50px 0px;
|
||||
height: 160px;
|
||||
width: 49.5%;
|
||||
float: right
|
||||
}
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100px!important;
|
||||
height: 100px!important;
|
||||
line-height: 100px!important;
|
||||
text-align: center;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
.thumb-div{
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
.item-infos{
|
||||
width: 30%;
|
||||
margin: auto
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
【微信消息 - 视频】
|
||||
芋道源码:
|
||||
① bug 修复:
|
||||
1)joolun 的做法:使用 mediaId 从微信公众号,下载对应的 mp4 素材,从而播放内容;
|
||||
存在的问题:mediaId 有效期是 3 天,超过时间后无法播放
|
||||
2)重构后的做法:后端接收到微信公众号的视频消息后,将视频消息的 media_id 的文件内容保存到文件服务器中,这样前端可以直接使用 URL 播放。
|
||||
② 体验优化:弹窗关闭后,自动暂停视频的播放
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<!-- 提示 -->
|
||||
<div @click="playVideo()">
|
||||
<i class="el-icon-video-play" style="font-size: 40px!important;" ></i>
|
||||
<p>点击播放视频</p>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗播放 -->
|
||||
<el-dialog title="视频播放" :visible.sync="dialogVideo" width="40%" append-to-body @close="closeDialog">
|
||||
<video-player v-if="playerOptions.sources[0].src" class="video-player vjs-custom-skin" ref="videoPlayer"
|
||||
:playsinline="true" :options="playerOptions"
|
||||
@play="onPlayerPlay($event)" @pause="onPlayerPause($event)">
|
||||
</video-player>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 引入 videoPlayer 相关组件。教程:https://juejin.cn/post/6923056942281654285
|
||||
import { videoPlayer } from 'vue-video-player'
|
||||
require('video.js/dist/video-js.css')
|
||||
require('vue-video-player/src/custom-theme.css')
|
||||
|
||||
export default {
|
||||
name: "wxVideoPlayer",
|
||||
props: {
|
||||
url: { // 视频地址,例如说:https://www.iocoder.cn/xxx.mp4
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
components: {
|
||||
videoPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVideo:false,
|
||||
playerOptions: {
|
||||
playbackRates: [0.5, 1.0, 1.5, 2.0], // 播放速度
|
||||
autoplay: false, // 如果 true,浏览器准备好时开始回放。
|
||||
muted: false, // 默认情况下将会消除任何音频。
|
||||
loop: false, // 导致视频一结束就重新开始。
|
||||
preload: 'auto', // 建议浏览器在 <video> 加载元素后是否应该开始下载视频数据。auto 浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
|
||||
language: 'zh-CN',
|
||||
aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
|
||||
fluid: true, // 当true时,Video.js player 将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
|
||||
sources: [{
|
||||
type: "video/mp4",
|
||||
src: "" // 你的视频地址(必填)【重要】
|
||||
}],
|
||||
poster: "", // 你的封面地址
|
||||
width: document.documentElement.clientWidth,
|
||||
notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖 Video.js 无法播放媒体源时显示的默认信息。
|
||||
controlBar: {
|
||||
timeDivider: true,
|
||||
durationDisplay: true,
|
||||
remainingTimeDisplay: false,
|
||||
fullscreenToggle: true //全屏按钮
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
playVideo(){
|
||||
this.dialogVideo = true
|
||||
// 设置地址
|
||||
this.$set(this.playerOptions.sources[0], 'src', this.url)
|
||||
},
|
||||
closeDialog(){
|
||||
// 暂停播放
|
||||
this.$refs.videoPlayer.player.pause()
|
||||
},
|
||||
onPlayerPlay(player) {
|
||||
},
|
||||
onPlayerPause(player) {
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,98 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
【微信消息 - 语音】
|
||||
芋道源码:
|
||||
① bug 修复:
|
||||
1)joolun 的做法:使用 mediaId 从微信公众号,下载对应的 mp4 素材,从而播放内容;
|
||||
存在的问题:mediaId 有效期是 3 天,超过时间后无法播放
|
||||
2)重构后的做法:后端接收到微信公众号的视频消息后,将视频消息的 media_id 的文件内容保存到文件服务器中,这样前端可以直接使用 URL 播放。
|
||||
② 代码优化:将 props 中的 objData 调成为 data 中对应的属性,并补充相关注释
|
||||
-->
|
||||
<template>
|
||||
<div class="wx-voice-div" @click="playVoice">
|
||||
<i :class="playing !== true ? 'el-icon-video-play': 'el-icon-video-pause'">
|
||||
<span class="amr-duration" v-if="duration">{{ duration }} 秒</span>
|
||||
</i>
|
||||
<div v-if="content">
|
||||
<el-tag type="success" size="mini">语音识别</el-tag>
|
||||
{{ content }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 因为微信语音是 amr 格式,所以需要用到 amr 解码器:https://www.npmjs.com/package/benz-amr-recorder
|
||||
const BenzAMRRecorder = require('benz-amr-recorder')
|
||||
|
||||
export default {
|
||||
name: "wxVoicePlayer",
|
||||
props: {
|
||||
url: { // 语音地址,例如说:https://www.iocoder.cn/xxx.amr
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
content: { // 语音文本
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
amr: undefined, // BenzAMRRecorder 对象
|
||||
playing: false, // 是否在播放中
|
||||
duration: undefined, // 播放时长
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
playVoice() {
|
||||
debugger
|
||||
// 情况一:未初始化,则创建 BenzAMRRecorder
|
||||
if (this.amr === undefined){
|
||||
this.amrInit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.amr.isPlaying()) {
|
||||
this.amrStop();
|
||||
} else {
|
||||
this.amrPlay();
|
||||
}
|
||||
},
|
||||
amrInit() {
|
||||
const amr = new BenzAMRRecorder();
|
||||
this.amr = amr;
|
||||
// 设置播放
|
||||
const that = this
|
||||
amr.initWithUrl(this.url).then(function() {
|
||||
that.amrPlay()
|
||||
that.duration = amr.getDuration();
|
||||
})
|
||||
// 监听暂停
|
||||
amr.onEnded(function() {
|
||||
that.playing = false;
|
||||
})
|
||||
},
|
||||
amrPlay() {
|
||||
this.playing = true;
|
||||
this.amr.play()
|
||||
},
|
||||
amrStop() {
|
||||
this.playing = false;
|
||||
this.amr.stop()
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wx-voice-div {
|
||||
padding: 5px;
|
||||
background-color: #eaeaea;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.amr-duration {
|
||||
font-size: 11px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
698
yudao-ui-admin/src/views/mp/draft/index.vue
Normal file
698
yudao-ui-admin/src/views/mp/draft/index.vue
Normal file
@@ -0,0 +1,698 @@
|
||||
<!--
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 www.joolun.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
芋道源码:
|
||||
① 优化代码,和项目的代码保持一致
|
||||
② 清理冗余代码,保证代码整洁
|
||||
③ 增加注释,提升可读性
|
||||
-->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['mp:draft:create']">新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<div class="waterfall" v-loading="loading">
|
||||
<div v-if="item.content && item.content.newsItem" class="waterfall-item" v-for="item in list"
|
||||
:key='item.articleId'>
|
||||
<wx-news :articles="item.content.newsItem" />
|
||||
<!-- 操作按钮 -->
|
||||
<el-row class="ope-row">
|
||||
<el-button type="success" circle @click="handlePublish(item)" v-hasPermi="['mp:free-publish:submit']">发布</el-button>
|
||||
<el-button type="primary" icon="el-icon-edit" circle @click="handleUpdate(item)" v-hasPermi="['mp:draft:update']" />
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)" v-hasPermi="['mp:draft:delete']" />
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页记录 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 添加或修改草稿对话框 -->
|
||||
<el-dialog :title="operateMaterial === 'add' ? '新建图文' : '修改图文'"
|
||||
append-to-body width="80%" top="20px" :visible.sync="dialogNewsVisible"
|
||||
:before-close="dialogNewsClose" :close-on-click-modal="false">
|
||||
<div class="left">
|
||||
<div class="select-item">
|
||||
<div v-for="(news, index) in articlesAdd" :key='news.id'>
|
||||
<div class="news-main father" v-if="index === 0" :class="{'activeAddNews': isActiveAddNews === index}"
|
||||
@click="activeNews(index)">
|
||||
<div class="news-content">
|
||||
<img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl"/>
|
||||
<div class="news-content-title">{{news.title}}</div>
|
||||
</div>
|
||||
<div class="child" v-if="articlesAdd.length>1">
|
||||
<el-button type="mini" icon="el-icon-sort-down" @click="downNews(index)">下移</el-button>
|
||||
<el-button v-if="operateMaterial === 'add'" type="mini" icon="el-icon-delete"
|
||||
@click="minusNews(index)">删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="news-main-item father" v-if="index>0" :class="{'activeAddNews': isActiveAddNews === index}"
|
||||
@click="activeNews(index)">
|
||||
<div class="news-content-item">
|
||||
<div class="news-content-item-title ">{{news.title}}</div>
|
||||
<div class="news-content-item-img">
|
||||
<img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl" height="100%"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="child">
|
||||
<el-button v-if="articlesAdd.length > index+1" type="mini" icon="el-icon-sort-down"
|
||||
@click="downNews(index)">下移
|
||||
</el-button>
|
||||
<el-button type="mini" icon="el-icon-sort-up" @click="upNews(index)">上移</el-button>
|
||||
<el-button v-if="operateMaterial=== 'add'" type="mini" icon="el-icon-delete"
|
||||
@click="minusNews(index)">删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="news-main-plus" @click="plusNews" v-if="articlesAdd.length<8 && operateMaterial==='add'">
|
||||
<i class="el-icon-circle-plus icon-plus"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0">
|
||||
<br /> <br /> <br /> <br />
|
||||
<!-- 标题、作者、原文地址 -->
|
||||
<el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题(必填)" />
|
||||
<el-input v-model="articlesAdd[isActiveAddNews].author" placeholder="请输入作者" style="margin-top: 5px;" />
|
||||
<el-input v-model="articlesAdd[isActiveAddNews].contentSourceUrl" placeholder="请输入原文地址" style="margin-top: 5px;" />
|
||||
<!-- 封面和摘要 -->
|
||||
<div class="input-tt">封面和摘要:</div>
|
||||
<div>
|
||||
<div class="thumb-div">
|
||||
<img class="material-img" v-if="articlesAdd[isActiveAddNews].thumbUrl"
|
||||
:src="articlesAdd[isActiveAddNews].thumbUrl" :class="isActiveAddNews === 0 ? 'avatar':'avatar1'">
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"
|
||||
:class="isActiveAddNews === 0 ? 'avatar':'avatar1'"></i>
|
||||
<div class="thumb-but">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:before-upload="beforeThumbImageUpload" :on-success="handleUploadSuccess">
|
||||
<el-button slot="trigger" size="mini" type="primary">本地上传</el-button>
|
||||
<el-button size="mini" type="primary" @click="openMaterial" style="margin-left: 5px">素材库选择</el-button>
|
||||
<div slot="tip" class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-dialog title="选择图片" :visible.sync="dialogImageVisible" width="80%" append-to-body>
|
||||
<wx-material-select ref="materialSelect" :objData="{type: 'image', accountId: this.queryParams.accountId}"
|
||||
@selectMaterial="selectMaterial" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
<el-input :rows="8" type="textarea" v-model="articlesAdd[isActiveAddNews].digest" placeholder="请输入摘要"
|
||||
class="digest" maxlength="120" style="float: right" />
|
||||
</div>
|
||||
<!--富文本编辑器组件-->
|
||||
<el-row>
|
||||
<wx-editor v-model="articlesAdd[isActiveAddNews].content" :account-id="this.uploadData.accountId"
|
||||
v-if="hackResetEditor"/>
|
||||
</el-row>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogNewsVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">提 交</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue';
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue';
|
||||
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
import {createDraft, deleteDraft, getDraftPage, updateDraft} from "@/api/mp/draft";
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
import {deleteFreePublish, submitFreePublish} from "@/api/mp/freePublish";
|
||||
|
||||
export default {
|
||||
name: 'mpDraft',
|
||||
components: {
|
||||
WxEditor,
|
||||
WxNews,
|
||||
WxMaterialSelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 数据列表
|
||||
list: [],
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
accountId: undefined,
|
||||
},
|
||||
|
||||
// ========== 文件上传 ==========
|
||||
actionUrl: process.env.VUE_APP_BASE_API + "/admin-api/mp/material/upload-permanent", // 上传永久素材的地址
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
fileList: [],
|
||||
uploadData: {
|
||||
"type": 'image',
|
||||
// "accountId": 1,
|
||||
},
|
||||
|
||||
// ========== 草稿新建 or 修改 ==========
|
||||
dialogNewsVisible: false,
|
||||
addMaterialLoading: false, // 添加草稿的 loading 标识
|
||||
articlesAdd: [],
|
||||
isActiveAddNews: 0,
|
||||
dialogImageVisible: false,
|
||||
operateMaterial: 'add',
|
||||
articlesMediaId: '',
|
||||
hackResetEditor: false,
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.setAccountId(this.accounts[0].id);
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// ======================== 列表查询 ========================
|
||||
/** 设置账号编号 */
|
||||
setAccountId(accountId) {
|
||||
this.queryParams.accountId = accountId;
|
||||
this.uploadData.accountId = accountId;
|
||||
},
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.queryParams.accountId) {
|
||||
this.$message.error('未选中公众号,无法查询草稿箱')
|
||||
return false
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
getDraftPage(this.queryParams).then(response => {
|
||||
// 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面
|
||||
response.data.list.forEach(item => {
|
||||
const newsItem = item.content.newsItem;
|
||||
newsItem.forEach(article => {
|
||||
article.picUrl = article.thumbUrl;
|
||||
})
|
||||
})
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1
|
||||
// 默认选中第一个
|
||||
if (this.queryParams.accountId) {
|
||||
this.setAccountId(this.queryParams.accountId)
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.setAccountId(this.accounts[0].id)
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
// ======================== 新增/修改草稿 ========================
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.resetEditor();
|
||||
this.reset();
|
||||
// 打开表单,并设置初始化
|
||||
this.operateMaterial = 'add'
|
||||
this.dialogNewsVisible = true
|
||||
},
|
||||
/** 更新按钮操作 */
|
||||
handleUpdate(item){
|
||||
this.resetEditor();
|
||||
this.reset();
|
||||
this.articlesMediaId = item.mediaId
|
||||
this.articlesAdd = JSON.parse(JSON.stringify(item.content.newsItem))
|
||||
// 打开表单,并设置初始化
|
||||
this.operateMaterial = 'edit'
|
||||
this.dialogNewsVisible = true
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.addMaterialLoading = true
|
||||
if (this.operateMaterial === 'add') {
|
||||
createDraft(this.queryParams.accountId, this.articlesAdd).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.dialogNewsVisible = false;
|
||||
this.getList()
|
||||
}).finally(() => {
|
||||
this.addMaterialLoading = false
|
||||
})
|
||||
} else {
|
||||
updateDraft(this.queryParams.accountId, this.articlesMediaId, this.articlesAdd).then(response => {
|
||||
this.$modal.msgSuccess("更新成功");
|
||||
this.dialogNewsVisible = false;
|
||||
this.getList()
|
||||
}).finally(() => {
|
||||
this.addMaterialLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
// 关闭弹窗
|
||||
dialogNewsClose(done) {
|
||||
this.$modal.confirm('修改内容可能还未保存,确定关闭吗?').then(() => {
|
||||
this.reset()
|
||||
this.resetEditor()
|
||||
done()
|
||||
}).catch(() => {})
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.isActiveAddNews = 0
|
||||
this.articlesAdd = [this.buildEmptyArticle()]
|
||||
},
|
||||
// 表单 Editor 重置
|
||||
resetEditor() {
|
||||
this.hackResetEditor = false // 销毁组件
|
||||
this.$nextTick(() => {
|
||||
this.hackResetEditor = true // 重建组件
|
||||
})
|
||||
},
|
||||
// 将图文向下移动
|
||||
downNews(index) {
|
||||
let temp = this.articlesAdd[index]
|
||||
this.articlesAdd[index] = this.articlesAdd[index+1]
|
||||
this.articlesAdd[index + 1] = temp
|
||||
this.isActiveAddNews = index + 1
|
||||
},
|
||||
// 将图文向上移动
|
||||
upNews(index) {
|
||||
let temp = this.articlesAdd[index]
|
||||
this.articlesAdd[index] = this.articlesAdd[index - 1]
|
||||
this.articlesAdd[index - 1] = temp
|
||||
this.isActiveAddNews = index - 1
|
||||
},
|
||||
// 选中指定 index 的图文
|
||||
activeNews(index) {
|
||||
this.resetEditor();
|
||||
this.isActiveAddNews = index
|
||||
},
|
||||
// 删除指定 index 的图文
|
||||
minusNews(index) {
|
||||
this.$modal.confirm('确定删除该图文吗?').then(() => {
|
||||
this.articlesAdd.splice(index,1);
|
||||
if (this.isActiveAddNews === index) {
|
||||
this.isActiveAddNews = 0
|
||||
}
|
||||
}).catch(() => {})
|
||||
},
|
||||
// 添加一个图文
|
||||
plusNews() {
|
||||
this.articlesAdd.push(this.buildEmptyArticle())
|
||||
this.isActiveAddNews = this.articlesAdd.length - 1
|
||||
},
|
||||
// 创建空的 article
|
||||
buildEmptyArticle() {
|
||||
return {
|
||||
"title": '',
|
||||
"thumbMediaId": '',
|
||||
"author": '',
|
||||
"digest": '',
|
||||
"showCoverPic": '',
|
||||
"content": '',
|
||||
"contentSourceUrl": '',
|
||||
"needOpenComment":'',
|
||||
"onlyFansCanComment":'',
|
||||
"thumbUrl":''
|
||||
}
|
||||
},
|
||||
|
||||
// ======================== 文件上传 ========================
|
||||
beforeThumbImageUpload(file) {
|
||||
this.addMaterialLoading = true
|
||||
const isType = file.type === 'image/jpeg'
|
||||
|| file.type === 'image/png'
|
||||
|| file.type === 'image/gif'
|
||||
|| file.type === 'image/bmp'
|
||||
|| file.type === 'image/jpg';
|
||||
if (!isType) {
|
||||
this.$message.error('上传图片格式不对!')
|
||||
this.addMaterialLoading = false
|
||||
return false;
|
||||
}
|
||||
const isLt = file.size / 1024 / 1024 < 2
|
||||
if (!isLt) {
|
||||
this.$message.error('上传图片大小不能超过 2M!')
|
||||
this.addMaterialLoading = false
|
||||
return false;
|
||||
}
|
||||
// 校验通过
|
||||
return true;
|
||||
},
|
||||
handleUploadSuccess(response, file, fileList) {
|
||||
this.addMaterialLoading = false // 关闭 loading
|
||||
if (response.code !== 0) {
|
||||
this.$message.error('上传出错:' + response.msg)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 重置上传文件的表单
|
||||
this.fileList = []
|
||||
|
||||
// 设置草稿的封面字段
|
||||
this.articlesAdd[this.isActiveAddNews].thumbMediaId = response.data.mediaId
|
||||
this.articlesAdd[this.isActiveAddNews].thumbUrl = response.data.url
|
||||
},
|
||||
// 选择 or 上传完素材,设置回草稿
|
||||
selectMaterial(item) {
|
||||
this.dialogImageVisible = false
|
||||
this.articlesAdd[this.isActiveAddNews].thumbMediaId = item.mediaId
|
||||
this.articlesAdd[this.isActiveAddNews].thumbUrl = item.url
|
||||
},
|
||||
// 打开素材选择
|
||||
openMaterial() {
|
||||
this.dialogImageVisible = true
|
||||
try {
|
||||
this.$refs['materialSelect'].queryParams.accountId = this.queryParams.accountId // 强制设置下 accountId,避免二次查询不对
|
||||
this.$refs['materialSelect'].handleQuery(); // 刷新列表,失败也无所谓
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// ======================== 草稿箱发布 ========================
|
||||
handlePublish(item) {
|
||||
const accountId = this.queryParams.accountId;
|
||||
const mediaId = item.mediaId;
|
||||
const content = '你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。';
|
||||
this.$modal.confirm(content).then(function() {
|
||||
return submitFreePublish(accountId, mediaId);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("发布成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
handleDelete(item) {
|
||||
const accountId = this.queryParams.accountId;
|
||||
const mediaId = item.mediaId;
|
||||
this.$modal.confirm('此操作将永久删除该草稿, 是否继续?').then(function() {
|
||||
return deleteDraft(accountId, mediaId);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pagination {
|
||||
float: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.add_but {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.ope-row {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #eaeaea;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
.left {
|
||||
display: inline-block;
|
||||
width: 35%;
|
||||
vertical-align: top;
|
||||
margin-top: 200px;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
width: 20%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload {
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: unset !important;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: #165dff;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 230px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.avatar1 {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.digest {
|
||||
width: 60%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap: 10px;
|
||||
column-count: 5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) and (max-width: 1300px) {
|
||||
.waterfall {
|
||||
column-count: 3;
|
||||
}
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
.waterfall {
|
||||
column-count: 2;
|
||||
}
|
||||
p {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*瀑布流样式*/
|
||||
.news-main {
|
||||
background-color: #FFFFFF;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.news-content {
|
||||
background-color: #acadae;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.news-content-title {
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
background-color: black;
|
||||
width: 98%;
|
||||
padding: 1%;
|
||||
opacity: 0.65;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.news-main-item {
|
||||
background-color: #FFFFFF;
|
||||
padding: 5px 0px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.news-content-item {
|
||||
position: relative;
|
||||
margin-left: -3px
|
||||
}
|
||||
|
||||
.news-content-item-title {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.news-content-item-img {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
background-color: #acadae
|
||||
}
|
||||
|
||||
.input-tt {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.activeAddNews {
|
||||
border: 5px solid #2bb673;
|
||||
}
|
||||
|
||||
.news-main-plus {
|
||||
width: 280px;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.icon-plus {
|
||||
margin: 10px;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.select-item {
|
||||
width: 60%;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.father .child {
|
||||
display: none;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
bottom: 25px;
|
||||
}
|
||||
|
||||
.father:hover .child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumb-div {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.thumb-but {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.material-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
393
yudao-ui-admin/src/views/mp/freePublish/index.vue
Normal file
393
yudao-ui-admin/src/views/mp/freePublish/index.vue
Normal file
@@ -0,0 +1,393 @@
|
||||
<!--
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 www.joolun.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
芋道源码:
|
||||
① 优化代码,和项目的代码保持一致
|
||||
-->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 列表 -->
|
||||
<div class="waterfall" v-loading="loading">
|
||||
<div v-if="item.content && item.content.newsItem" class="waterfall-item" v-for="item in list"
|
||||
:key='item.articleId'>
|
||||
<wx-news :articles="item.content.newsItem" />
|
||||
<!-- 操作 -->
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)"
|
||||
v-hasPermi="['mp:free-publish:delete']" />
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFreePublishPage, deleteFreePublish } from "@/api/mp/freePublish";
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue';
|
||||
|
||||
export default {
|
||||
name: 'mpDraft',
|
||||
components: {
|
||||
WxNews
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 已发表列表
|
||||
list: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
total: 0, // 总页数
|
||||
currentPage: 1, // 当前页数
|
||||
queryParamsSize: 10 // 每页显示多少条
|
||||
},
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.queryParams.accountId) {
|
||||
this.$message.error('未选中公众号,无法查询已发表图文')
|
||||
return false
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
getFreePublishPage(this.queryParams).then(response => {
|
||||
// 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面
|
||||
response.data.list.forEach(item => {
|
||||
const newsItem = item.content.newsItem;
|
||||
newsItem.forEach(article => {
|
||||
article.picUrl = article.thumbUrl;
|
||||
})
|
||||
})
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(item){
|
||||
const articleId = item.articleId;
|
||||
const accountId = this.queryParams.accountId;
|
||||
this.$modal.confirm('删除后用户将无法访问此页面,确定删除?').then(function() {
|
||||
return deleteFreePublish(accountId, articleId);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pagination {
|
||||
float: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.add_but {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.ope-row {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #eaeaea;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
.left {
|
||||
display: inline-block;
|
||||
width: 35%;
|
||||
vertical-align: top;
|
||||
margin-top: 200px;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
width: 20%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload {
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: unset !important;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: #165dff;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #d9d9d9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 230px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.avatar1 {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.digest {
|
||||
width: 60%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/*新增图文*/
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap: 10px;
|
||||
column-count: 5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) and (max-width: 1300px) {
|
||||
.waterfall {
|
||||
column-count: 3;
|
||||
}
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
.waterfall {
|
||||
column-count: 2;
|
||||
}
|
||||
p {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*瀑布流样式*/
|
||||
.news-main {
|
||||
background-color: #FFFFFF;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.news-content {
|
||||
background-color: #acadae;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.news-content-title {
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
background-color: black;
|
||||
width: 98%;
|
||||
padding: 1%;
|
||||
opacity: 0.65;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.news-main-item {
|
||||
background-color: #FFFFFF;
|
||||
padding: 5px 0px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.news-content-item {
|
||||
position: relative;
|
||||
margin-left: -3px
|
||||
}
|
||||
|
||||
.news-content-item-title {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.news-content-item-img {
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
background-color: #acadae
|
||||
}
|
||||
|
||||
.input-tt {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.activeAddNews {
|
||||
border: 5px solid #2bb673;
|
||||
}
|
||||
|
||||
.news-main-plus {
|
||||
width: 280px;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.icon-plus {
|
||||
margin: 10px;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.select-item {
|
||||
width: 60%;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.father .child {
|
||||
display: none;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
bottom: 25px;
|
||||
}
|
||||
|
||||
.father:hover .child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumb-div {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.thumb-but {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.material-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
438
yudao-ui-admin/src/views/mp/material/index.vue
Normal file
438
yudao-ui-admin/src/views/mp/material/index.vue
Normal file
@@ -0,0 +1,438 @@
|
||||
<!--
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 www.joolun.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
芋道源码:
|
||||
① 清理冗余 css 内容,清理冗余 data 变量
|
||||
② 美化样式,支持播放,提升使用体验
|
||||
③ 优化代码,特别是方法名和变量,提升可读性
|
||||
-->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-tabs v-model="type" @tab-click="handleClick">
|
||||
<!-- tab 1:图片 -->
|
||||
<el-tab-pane name="image">
|
||||
<span slot="label"><i class="el-icon-picture"></i> 图片</span>
|
||||
<div class="add_but" v-hasPermi="['mp:material:upload-permanent']">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:before-upload="beforeImageUpload" :on-success="handleUploadSuccess">
|
||||
<el-button size="mini" type="primary">点击上传</el-button>
|
||||
<sapn slot="tip" class="el-upload__tip" style="margin-left: 5px">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</sapn>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="waterfall" v-loading="loading">
|
||||
<div class="waterfall-item" v-for="item in list" :key='item.id'>
|
||||
<a target="_blank" :href="item.url">
|
||||
<img class="material-img" :src="item.url">
|
||||
<div class="item-name">{{item.name}}</div>
|
||||
</a>
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)"
|
||||
v-hasPermi="['mp:material:delete']"/>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- tab 2:语音 -->
|
||||
<el-tab-pane name="voice">
|
||||
<span slot="label"><i class="el-icon-microphone"></i> 语音</span>
|
||||
<div class="add_but" v-hasPermi="['mp:material:upload-permanent']">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:on-success="handleUploadSuccess" :before-upload="beforeVoiceUpload">
|
||||
<el-button size="mini" type="primary">点击上传</el-button>
|
||||
<span slot="tip" class="el-upload__tip" style="margin-left: 5px">格式支持 mp3/wma/wav/amr,文件大小不超过 2M,播放长度不超过 60s</span>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-table :data="list" stripe border v-loading="loading" style="margin-top: 10px;">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="语音" align="center">
|
||||
<template v-slot="scope">
|
||||
<wx-voice-player :url="scope.row.url" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" icon="el-icon-download" size="small" plain @click="handleDownload(scope.row)">下载</el-button>
|
||||
<el-button type="text" icon="el-icon-delete" size="small" plain @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['mp:material:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- tab 3:视频 -->
|
||||
<el-tab-pane name="video">
|
||||
<span slot="label"><i class="el-icon-video-play"></i> 视频</span>
|
||||
<div class="add_but" v-hasPermi="['mp:material:upload-permanent']">
|
||||
<el-button size="mini" type="primary" @click="handleAddVideo">新建视频</el-button>
|
||||
</div>
|
||||
<!-- 新建视频的弹窗 -->
|
||||
<el-dialog title="新建视频" :visible.sync="dialogVideoVisible" append-to-body width="600px"
|
||||
v-loading="addMaterialLoading">
|
||||
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
|
||||
:before-upload="beforeVideoUpload" :on-success="handleUploadSuccess"
|
||||
ref="uploadVideo" :auto-upload="false">
|
||||
<el-button slot="trigger" size="mini" type="primary">选择视频</el-button>
|
||||
<span class="el-upload__tip" style="margin-left: 10px;">格式支持 MP4,文件大小不超过 10MB</span>
|
||||
</el-upload>
|
||||
<el-form :model="uploadData" :rules="uploadRules" ref="uploadForm" label-width="80px">
|
||||
<el-row>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="uploadData.title" placeholder="标题将展示在相关播放页面,建议填写清晰、准确、生动的标题" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-form-item label="描述" prop="introduction">
|
||||
<el-input :rows="3" type="textarea" v-model="uploadData.introduction"
|
||||
placeholder="介绍语将展示在相关播放页面,建议填写简洁明确、有信息量的内容" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="cancelVideo">取 消</el-button>
|
||||
<el-button type="primary" @click="submitVideo">提 交</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-table :data="list" stripe border v-loading="loading" style="margin-top: 10px;">
|
||||
<el-table-column label="编号" align="center" prop="mediaId" />
|
||||
<el-table-column label="文件名" align="center" prop="name" />
|
||||
<el-table-column label="标题" align="center" prop="title" />
|
||||
<el-table-column label="介绍" align="center" prop="introduction" />
|
||||
<el-table-column label="视频" align="center">
|
||||
<template v-slot="scope">
|
||||
<wx-video-player :url="scope.row.url" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" icon="el-icon-download" size="small" plain @click="handleDownload(scope.row)">下载</el-button>
|
||||
<el-button type="text" icon="el-icon-delete" size="small" plain @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['mp:material:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
|
||||
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
import { getMaterialPage, deletePermanentMaterial } from "@/api/mp/material";
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
name: 'mpMaterial',
|
||||
components: {
|
||||
WxVoicePlayer,
|
||||
WxVideoPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: 'image',
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 数据列表
|
||||
list: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
accountId: undefined,
|
||||
permanent: true,
|
||||
},
|
||||
|
||||
actionUrl: process.env.VUE_APP_BASE_API + '/admin-api/mp/material/upload-permanent',
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
fileList:[],
|
||||
uploadData: {
|
||||
"type": 'image',
|
||||
"title":'',
|
||||
"introduction":''
|
||||
},
|
||||
// === 视频上传,独有变量 ===
|
||||
dialogVideoVisible: false,
|
||||
addMaterialLoading: false,
|
||||
uploadRules: { // 视频上传的校验规则
|
||||
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
|
||||
introduction: [{ required: true, message: '请输入描述', trigger: 'blur' }],
|
||||
},
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.setAccountId(this.accounts[0].id);
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// ======================== 列表查询 ========================
|
||||
/** 设置账号编号 */
|
||||
setAccountId(accountId) {
|
||||
this.queryParams.accountId = accountId;
|
||||
this.uploadData.accountId = accountId;
|
||||
},
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.queryParams.accountId) {
|
||||
this.$message.error('未选中公众号,无法查询草稿箱')
|
||||
return false
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
getMaterialPage({
|
||||
...this.queryParams,
|
||||
type: this.type
|
||||
}).then(response => {
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1
|
||||
// 默认选中第一个
|
||||
if (this.queryParams.accountId) {
|
||||
this.setAccountId(this.queryParams.accountId)
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.setAccountId(this.accounts[0].id)
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
// 设置 type
|
||||
this.uploadData.type = tab.name
|
||||
// 从第一页开始查询
|
||||
this.handleQuery();
|
||||
},
|
||||
|
||||
// ======================== 文件上传 ========================
|
||||
beforeImageUpload(file) {
|
||||
const isType = file.type === 'image/jpeg'
|
||||
|| file.type === 'image/png'
|
||||
|| file.type === 'image/gif'
|
||||
|| file.type === 'image/bmp'
|
||||
|| file.type === 'image/jpg';
|
||||
if (!isType) {
|
||||
this.$message.error('上传图片格式不对!')
|
||||
return false;
|
||||
}
|
||||
const isLt = file.size / 1024 / 1024 < 2
|
||||
if (!isLt) {
|
||||
this.$message.error('上传图片大小不能超过 2M!')
|
||||
return false;
|
||||
}
|
||||
this.loading = true
|
||||
return true;
|
||||
},
|
||||
beforeVoiceUpload(file){
|
||||
const isType = file.type === 'audio/mp3'
|
||||
|| file.type === 'audio/wma'
|
||||
|| file.type === 'audio/wav'
|
||||
|| file.type === 'audio/amr';
|
||||
const isLt = file.size / 1024 / 1024 < 2
|
||||
if (!isType) {
|
||||
this.$message.error('上传语音格式不对!')
|
||||
return false;
|
||||
}
|
||||
if (!isLt) {
|
||||
this.$message.error('上传语音大小不能超过 2M!')
|
||||
return false;
|
||||
}
|
||||
this.loading = true
|
||||
return true;
|
||||
},
|
||||
beforeVideoUpload(file){
|
||||
const isType = file.type === 'video/mp4'
|
||||
if (!isType) {
|
||||
this.$message.error('上传视频格式不对!')
|
||||
return false;
|
||||
}
|
||||
const isLt = file.size / 1024 / 1024 < 10
|
||||
if (!isLt) {
|
||||
this.$message.error('上传视频大小不能超过 10M!')
|
||||
return false
|
||||
}
|
||||
this.addMaterialLoading = true
|
||||
return true
|
||||
},
|
||||
handleUploadSuccess(response, file, fileList) {
|
||||
this.loading = false
|
||||
this.addMaterialLoading = false
|
||||
if (response.code !== 0) {
|
||||
this.$message.error('上传出错:' + response.msg)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清空上传时的各种数据
|
||||
this.dialogVideoVisible = false
|
||||
this.fileList = []
|
||||
this.uploadData.title = ''
|
||||
this.uploadData.introduction = ''
|
||||
|
||||
// 加载数据
|
||||
this.getList()
|
||||
},
|
||||
// 下载文件
|
||||
handleDownload(row) {
|
||||
window.open(row.url,'_blank')
|
||||
},
|
||||
// 提交 video 新建的表单
|
||||
submitVideo() {
|
||||
this.$refs['uploadForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
this.$refs.uploadVideo.submit()
|
||||
})
|
||||
},
|
||||
handleAddVideo() {
|
||||
this.resetVideo();
|
||||
this.dialogVideoVisible = true
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancelVideo() {
|
||||
this.dialogVideoVisible = false;
|
||||
this.resetVideo();
|
||||
},
|
||||
/** 表单重置 */
|
||||
resetVideo() {
|
||||
this.fileList = []
|
||||
this.uploadData.title = ''
|
||||
this.uploadData.introduction = ''
|
||||
this.resetForm("uploadForm");
|
||||
},
|
||||
|
||||
// ======================== 其它操作 ========================
|
||||
handleDelete(item) {
|
||||
const id = item.id
|
||||
this.$modal.confirm('此操作将永久删除该文件, 是否继续?').then(function() {
|
||||
return deletePermanentMaterial(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/*瀑布流样式*/
|
||||
.waterfall {
|
||||
width: 100%;
|
||||
column-gap:10px;
|
||||
column-count: 5;
|
||||
margin-top: 10px; /* 芋道源码:增加 10px,避免顶着上面 */
|
||||
}
|
||||
.waterfall-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
break-inside: avoid;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.material-img {
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
line-height: 30px;
|
||||
}
|
||||
@media (min-width: 992px) and (max-width: 1300px) {
|
||||
.waterfall {
|
||||
column-count: 3;
|
||||
}
|
||||
p {
|
||||
color:red;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
.waterfall {
|
||||
column-count: 2;
|
||||
}
|
||||
p {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.waterfall {
|
||||
column-count: 1;
|
||||
}
|
||||
}
|
||||
/*瀑布流样式*/
|
||||
</style>
|
||||
BIN
yudao-ui-admin/src/views/mp/menu/assets/demo.png
Normal file
BIN
yudao-ui-admin/src/views/mp/menu/assets/demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
yudao-ui-admin/src/views/mp/menu/assets/iphone_backImg.png
Normal file
BIN
yudao-ui-admin/src/views/mp/menu/assets/iphone_backImg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
yudao-ui-admin/src/views/mp/menu/assets/logo.png
Normal file
BIN
yudao-ui-admin/src/views/mp/menu/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
BIN
yudao-ui-admin/src/views/mp/menu/assets/menu_foot.png
Normal file
BIN
yudao-ui-admin/src/views/mp/menu/assets/menu_foot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
yudao-ui-admin/src/views/mp/menu/assets/menu_head.png
Normal file
BIN
yudao-ui-admin/src/views/mp/menu/assets/menu_head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
712
yudao-ui-admin/src/views/mp/menu/index.vue
Normal file
712
yudao-ui-admin/src/views/mp/menu/index.vue
Normal file
@@ -0,0 +1,712 @@
|
||||
<!--
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 www.joolun.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
芋道源码:
|
||||
① less 切到 scss,减少对 less 和 less-loader 的依赖
|
||||
②
|
||||
-->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="public-account-management clearfix" v-loading="loading">
|
||||
<!--左边配置菜单-->
|
||||
<div class="left">
|
||||
<div class="weixin-hd">
|
||||
<div class="weixin-title">{{ name }}</div>
|
||||
</div>
|
||||
<div class="weixin-menu menu_main clearfix">
|
||||
<div class="menu_bottom" v-for="(item, i) of menuList" :key="i" >
|
||||
<!-- 一级菜单 -->
|
||||
<div @click="menuClick(i, item)" class="menu_item el-icon-s-fold" :class="{'active': isActive === i}">{{item.name}}</div>
|
||||
<!-- 以下为二级菜单-->
|
||||
<div class="submenu" v-if="isSubMenuFlag === i">
|
||||
<div class="subtitle menu_bottom" v-if="item.children" v-for="(subItem, k) in item.children" :key="k">
|
||||
<div class="menu_subItem" :class="{'active': isSubMenuActive === i + '' + k}" @click="subMenuClick(subItem, i, k)">
|
||||
{{subItem.name}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 二级菜单加号, 当长度 小于 5 才显示二级菜单的加号 -->
|
||||
<div class="menu_bottom menu_addicon" v-if="!item.children || item.children.length < 5" @click="addSubMenu(i,item)">
|
||||
<i class="el-icon-plus" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 一级菜单加号 -->
|
||||
<div class="menu_bottom menu_addicon" v-if="this.menuList.length < 3" @click="addMenu"><i class="el-icon-plus"></i></div>
|
||||
</div>
|
||||
<div class="save_div">
|
||||
<el-button class="save_btn" type="success" size="small" @click="handleSave" v-hasPermi="['mp:menu:save']">保存并发布菜单</el-button>
|
||||
<el-button class="save_btn" type="danger" size="small" @click="handleDelete" v-hasPermi="['mp:menu:delete']">清空菜单</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!--右边配置-->
|
||||
<div v-if="showRightFlag" class="right">
|
||||
<div class="configure_page">
|
||||
<div class="delete_btn">
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteMenu(tempObj)">删除当前菜单</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<span>菜单名称:</span>
|
||||
<el-input class="input_width" v-model="tempObj.name" placeholder="请输入菜单名称" :maxlength="nameMaxLength" clearable />
|
||||
</div>
|
||||
<div v-if="showConfigureContent">
|
||||
<div class="menu_content">
|
||||
<span>菜单标识:</span>
|
||||
<el-input class="input_width" v-model="tempObj.menuKey" placeholder="请输入菜单 KEY" clearable />
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<span>菜单内容:</span>
|
||||
<el-select v-model="tempObj.type" clearable placeholder="请选择" class="menu_option">
|
||||
<el-option v-for="item in menuOptions" :label="item.label" :value="item.value" :key="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'view'">
|
||||
<span>跳转链接:</span>
|
||||
<el-input class="input_width" v-model="tempObj.url" placeholder="请输入链接" clearable />
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'miniprogram'">
|
||||
<div class="applet">
|
||||
<span>小程序的 appid :</span>
|
||||
<el-input class="input_width" v-model="tempObj.miniProgramAppId" placeholder="请输入小程序的appid" clearable />
|
||||
</div>
|
||||
<div class="applet">
|
||||
<span>小程序的页面路径:</span>
|
||||
<el-input class="input_width" v-model="tempObj.miniProgramPagePath"
|
||||
placeholder="请输入小程序的页面路径,如:pages/index" clearable />
|
||||
</div>
|
||||
<div class="applet">
|
||||
<span>小程序的备用网页:</span>
|
||||
<el-input class="input_width" v-model="tempObj.url" placeholder="不支持小程序的老版本客户端将打开本网页" clearable />
|
||||
</div>
|
||||
<p class="blue">tips:需要和公众号进行关联才可以把小程序绑定带微信菜单上哟!</p>
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'article_view_limited'">
|
||||
<el-row>
|
||||
<div class="select-item" v-if="tempObj && tempObj.replyArticles">
|
||||
<wx-news :articles="tempObj.replyArticles" />
|
||||
<el-row class="ope-row">
|
||||
<el-button type="danger" icon="el-icon-delete" circle @click="deleteMaterial" />
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-row>
|
||||
<el-col :span="24" style="text-align: center">
|
||||
<el-button type="success" @click="openMaterial">
|
||||
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-dialog title="选择图文" :visible.sync="dialogNewsVisible" width="90%">
|
||||
<wx-material-select :objData="{type: 'news', accountId: this.accountId}" @selectMaterial="selectMaterial" />
|
||||
</el-dialog>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="configur_content" v-if="tempObj.type === 'click' || tempObj.type === 'scancode_waitmsg'">
|
||||
<wx-reply-select :objData="tempObj.reply" v-if="hackResetWxReplySelect" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 一进页面就显示的默认页面,当点击左边按钮的时候,就不显示了-->
|
||||
<div v-else class="right">
|
||||
<p>请选择菜单配置</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue';
|
||||
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
|
||||
import { deleteMenu, getMenuList, saveMenu } from "@/api/mp/menu";
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
|
||||
export default {
|
||||
name: 'mpMenu',
|
||||
components: {
|
||||
WxReplySelect,
|
||||
WxNews,
|
||||
WxMaterialSelect
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
// ======================== 列表查询 ========================
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 查询参数
|
||||
accountId: undefined,
|
||||
name:'', // 公众号名
|
||||
menuList: {
|
||||
children: [],
|
||||
},
|
||||
|
||||
// ======================== 菜单操作 ========================
|
||||
isActive: -1,// 一级菜单点中样式
|
||||
isSubMenuActive: -1, // 一级菜单点中样式
|
||||
isSubMenuFlag: -1, // 二级菜单显示标志
|
||||
|
||||
// ======================== 菜单编辑 ========================
|
||||
showRightFlag: false, // 右边配置显示默认详情还是配置详情
|
||||
nameMaxLength: 0, // 菜单名称最大长度;1 级是 4 字符;2 级是 7 字符;
|
||||
showConfigureContent: true, // 是否展示配置内容;如果有子菜单,就不显示配置内容
|
||||
hackResetWxReplySelect: false, // 重置 WxReplySelect 组件
|
||||
tempObj: {}, // 右边临时变量,作为中间值牵引关系
|
||||
tempSelfObj: { // 一些临时值放在这里进行判断,如果放在 tempObj,由于引用关系,menu 也会多了多余的参数
|
||||
},
|
||||
dialogNewsVisible: false, // 跳转图文时的素材选择弹窗
|
||||
menuOptions: [{
|
||||
value: 'view',
|
||||
label: '跳转网页'
|
||||
}, {
|
||||
value: 'miniprogram',
|
||||
label: '跳转小程序'
|
||||
}, {
|
||||
value: 'click',
|
||||
label: '点击回复'
|
||||
}, {
|
||||
value: 'article_view_limited',
|
||||
label: '跳转图文消息'
|
||||
}, {
|
||||
value: 'scancode_push',
|
||||
label: '扫码直接返回结果'
|
||||
}, {
|
||||
value: 'scancode_waitmsg',
|
||||
label: '扫码回复'
|
||||
}, {
|
||||
value: 'pic_sysphoto',
|
||||
label: '系统拍照发图'
|
||||
}, {
|
||||
value: 'pic_photo_or_album',
|
||||
label: '拍照或者相册'
|
||||
}, {
|
||||
value: 'pic_weixin',
|
||||
label: '微信相册'
|
||||
}, {
|
||||
value: 'location_select',
|
||||
label: '选择地理位置'
|
||||
}],
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.setAccountId(this.accounts[0].id);
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// ======================== 列表查询 ========================
|
||||
/** 设置账号编号 */
|
||||
setAccountId(accountId) {
|
||||
this.accountId = accountId;
|
||||
this.name = this.accounts.find(item => item.id === accountId)?.name;
|
||||
},
|
||||
getList() {
|
||||
this.loading = false;
|
||||
getMenuList(this.accountId).then(response => {
|
||||
response.data = this.convertMenuList(response.data);
|
||||
this.menuList = this.handleTree(response.data, "id");
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.resetForm();
|
||||
// 默认选中第一个
|
||||
if (this.accountId) {
|
||||
this.setAccountId(this.accountId)
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm();
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.setAccountId(this.accounts[0].id)
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
// 将后端返回的 menuList,转换成前端的 menuList
|
||||
convertMenuList(list) {
|
||||
const menuList = [];
|
||||
list.forEach(item => {
|
||||
const menu = {
|
||||
...item,
|
||||
};
|
||||
if (item.type === 'click' || item.type === 'scancode_waitmsg') {
|
||||
this.$delete(menu, 'replyMessageType');
|
||||
this.$delete(menu, 'replyContent');
|
||||
this.$delete(menu, 'replyMediaId');
|
||||
this.$delete(menu, 'replyMediaUrl');
|
||||
this.$delete(menu, 'replyDescription');
|
||||
this.$delete(menu, 'replyArticles');
|
||||
menu.reply = {
|
||||
type: item.replyMessageType,
|
||||
accountId: item.accountId,
|
||||
content: item.replyContent,
|
||||
mediaId: item.replyMediaId,
|
||||
url: item.replyMediaUrl,
|
||||
title: item.replyTitle,
|
||||
description: item.replyDescription,
|
||||
thumbMediaId: item.replyThumbMediaId,
|
||||
thumbMediaUrl: item.replyThumbMediaUrl,
|
||||
articles: item.replyArticles,
|
||||
musicUrl: item.replyMusicUrl,
|
||||
hqMusicUrl: item.replyHqMusicUrl,
|
||||
}
|
||||
}
|
||||
menuList.push(menu);
|
||||
});
|
||||
return menuList;
|
||||
},
|
||||
// 重置表单,清空表单数据
|
||||
resetForm() {
|
||||
// 菜单操作
|
||||
this.isActive = -1;
|
||||
this.isSubMenuActive = -1;
|
||||
this.isSubMenuFlag = -1;
|
||||
|
||||
// 菜单编辑
|
||||
this.showRightFlag = false;
|
||||
this.nameMaxLength = 0;
|
||||
this.showConfigureContent = 0;
|
||||
this.hackResetWxReplySelect = true;
|
||||
this.hackResetWxReplySelect = false;
|
||||
this.tempObj = {};
|
||||
this.tempSelfObj = {};
|
||||
this.dialogNewsVisible = false;
|
||||
},
|
||||
|
||||
// ======================== 菜单操作 ========================
|
||||
// 一级菜单点击事件
|
||||
menuClick(i, item) {
|
||||
// 右侧的表单相关
|
||||
this.resetEditor();
|
||||
this.showRightFlag = true; // 右边菜单
|
||||
this.tempObj = item; // 这个如果放在顶部,flag 会没有。因为重新赋值了。
|
||||
this.tempSelfObj.grand = "1"; // 表示一级菜单
|
||||
this.tempSelfObj.index = i; // 表示一级菜单索引
|
||||
this.nameMaxLength = 4
|
||||
this.showConfigureContent = !(item.children && item.children.length > 0); // 有子菜单,就不显示配置内容
|
||||
|
||||
// 左侧的选中
|
||||
this.isActive = i; // 一级菜单选中样式
|
||||
this.isSubMenuFlag = i; // 二级菜单显示标志
|
||||
this.isSubMenuActive = -1; // 二级菜单去除选中样式
|
||||
},
|
||||
// 二级菜单点击事件
|
||||
subMenuClick(subItem, index, k) {
|
||||
// 右侧的表单相关
|
||||
this.resetEditor();
|
||||
this.showRightFlag = true; // 右边菜单
|
||||
this.tempObj = subItem; // 将点击的数据放到临时变量,对象有引用作用
|
||||
this.tempSelfObj.grand = "2"; // 表示二级菜单
|
||||
this.tempSelfObj.index = index; // 表示一级菜单索引
|
||||
this.tempSelfObj.secondIndex = k; // 表示二级菜单索引
|
||||
this.nameMaxLength = 7
|
||||
this.showConfigureContent = true;
|
||||
|
||||
// 左侧的选中
|
||||
this.isActive = -1; // 一级菜单去除样式
|
||||
this.isSubMenuActive = index + "" + k; // 二级菜单选中样式
|
||||
},
|
||||
// 添加横向一级菜单
|
||||
addMenu() {
|
||||
const menuKeyLength = this.menuList.length;
|
||||
const addButton = {
|
||||
name: "菜单名称",
|
||||
children: [],
|
||||
reply: { // 用于存储回复内容
|
||||
'type': 'text',
|
||||
'accountId': this.accountId // 保证组件里,可以使用到对应的公众号
|
||||
}
|
||||
}
|
||||
this.$set(this.menuList, menuKeyLength, addButton)
|
||||
this.menuClick(this.menuKeyLength - 1, addButton)
|
||||
},
|
||||
// 添加横向二级菜单;item 表示要操作的父菜单
|
||||
addSubMenu(i, item) {
|
||||
// 清空父菜单的属性,因为它只需要 name 属性即可
|
||||
if (!item.children || item.children.length <= 0) {
|
||||
this.$set( item, 'children',[])
|
||||
this.$delete( item, 'type')
|
||||
this.$delete( item, 'menuKey')
|
||||
this.$delete( item, 'miniProgramAppId')
|
||||
this.$delete( item, 'miniProgramPagePath')
|
||||
this.$delete( item, 'url')
|
||||
this.$delete( item, 'reply')
|
||||
this.$delete( item, 'articleId')
|
||||
this.$delete( item, 'replyArticles')
|
||||
// 关闭配置面板
|
||||
this.showConfigureContent = false
|
||||
}
|
||||
|
||||
let subMenuKeyLength = item.children.length; // 获取二级菜单key长度
|
||||
let addButton = {
|
||||
name: "子菜单名称",
|
||||
reply: { // 用于存储回复内容
|
||||
'type': 'text',
|
||||
'accountId': this.accountId // 保证组件里,可以使用到对应的公众号
|
||||
}
|
||||
}
|
||||
this.$set(item.children, subMenuKeyLength, addButton);
|
||||
this.subMenuClick(item.children[subMenuKeyLength], i, subMenuKeyLength)
|
||||
},
|
||||
// 删除当前菜单
|
||||
deleteMenu(item) {
|
||||
this.$modal.confirm('确定要删除吗?').then(() => {
|
||||
// 删除数据
|
||||
if (this.tempSelfObj.grand === "1") { // 一级菜单的删除方法
|
||||
this.menuList.splice(this.tempSelfObj.index, 1);
|
||||
} else if (this.tempSelfObj.grand === "2") { // 二级菜单的删除方法
|
||||
this.menuList[this.tempSelfObj.index].children.splice(this.tempSelfObj.secondIndex, 1);
|
||||
}
|
||||
// 提示
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
|
||||
// 处理菜单的选中
|
||||
this.tempObj = {};
|
||||
this.showRightFlag = false;
|
||||
this.isActive = -1;
|
||||
this.isSubMenuActive = -1;
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
// ======================== 菜单编辑 ========================
|
||||
handleSave() {
|
||||
this.$modal.confirm('确定要保证并发布该菜单吗?').then(() => {
|
||||
this.loading = true
|
||||
return saveMenu(this.accountId, this.convertMenuFormList());
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("发布成功");
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
});
|
||||
},
|
||||
// 表单 Editor 重置
|
||||
resetEditor() {
|
||||
this.hackResetWxReplySelect = false // 销毁组件
|
||||
this.$nextTick(() => {
|
||||
this.hackResetWxReplySelect = true // 重建组件
|
||||
})
|
||||
},
|
||||
handleDelete() {
|
||||
this.$modal.confirm('确定要清空所有菜单吗?').then(() => {
|
||||
this.loading = true
|
||||
return deleteMenu(this.accountId);
|
||||
}).then(() => {
|
||||
this.handleQuery();
|
||||
this.$modal.msgSuccess("清空成功");
|
||||
}).catch(() => {}).finally(() => {
|
||||
this.loading = false
|
||||
});
|
||||
},
|
||||
// 将前端的 menuList,转换成后端接收的 menuList
|
||||
convertMenuFormList() {
|
||||
const menuList = [];
|
||||
this.menuList.forEach(item => {
|
||||
let menu = this.convertMenuForm(item);
|
||||
menuList.push(menu);
|
||||
// 处理子菜单
|
||||
if (!item.children || item.children.length <= 0) {
|
||||
return;
|
||||
}
|
||||
menu.children = [];
|
||||
item.children.forEach(subItem => {
|
||||
menu.children.push(this.convertMenuForm(subItem))
|
||||
})
|
||||
})
|
||||
return menuList;
|
||||
},
|
||||
// 将前端的 menu,转换成后端接收的 menu
|
||||
convertMenuForm(menu) {
|
||||
let result = {
|
||||
...menu,
|
||||
children: undefined, // 不处理子节点
|
||||
reply: undefined, // 稍后复制
|
||||
}
|
||||
if (menu.type === 'click' || menu.type === 'scancode_waitmsg') {
|
||||
result.replyMessageType = menu.reply.type;
|
||||
result.replyContent = menu.reply.content;
|
||||
result.replyMediaId = menu.reply.mediaId;
|
||||
result.replyMediaUrl = menu.reply.url;
|
||||
result.replyTitle = menu.reply.title;
|
||||
result.replyDescription = menu.reply.description;
|
||||
result.replyThumbMediaId = menu.reply.thumbMediaId;
|
||||
result.replyThumbMediaUrl = menu.reply.thumbMediaUrl;
|
||||
result.replyArticles = menu.reply.articles;
|
||||
result.replyMusicUrl = menu.reply.musicUrl;
|
||||
result.replyHqMusicUrl = menu.reply.hqMusicUrl;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
// ======================== 菜单编辑(素材选择) ========================
|
||||
openMaterial() {
|
||||
this.dialogNewsVisible = true
|
||||
},
|
||||
selectMaterial(item) {
|
||||
const articleId = item.articleId;
|
||||
const articles = item.content.newsItem;
|
||||
// 提示,针对多图文
|
||||
if (articles.length > 1) {
|
||||
this.$alert('您选择的是多图文,将默认跳转第一篇', '提示', {
|
||||
confirmButtonText: '确定'
|
||||
})
|
||||
}
|
||||
this.dialogNewsVisible = false
|
||||
|
||||
// 设置菜单的回复
|
||||
this.tempObj.articleId = articleId;
|
||||
this.tempObj.replyArticles = [];
|
||||
articles.forEach(article => {
|
||||
this.tempObj.replyArticles.push({
|
||||
title: article.title,
|
||||
description: article.digest,
|
||||
picUrl: article.picUrl,
|
||||
url: article.url,
|
||||
})
|
||||
})
|
||||
},
|
||||
deleteMaterial() {
|
||||
this.$delete(this.tempObj,'articleId')
|
||||
this.$delete(this.tempObj,'replyArticles')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<!--本组件样式-->
|
||||
<style lang="scss" scoped="scoped">
|
||||
/* 公共颜色变量 */
|
||||
.clearfix{*zoom:1;}
|
||||
.clearfix::after{content: "";display: table; clear: both;}
|
||||
div{
|
||||
text-align: left;
|
||||
}
|
||||
.weixin-hd{
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
bottom: 426px;
|
||||
left:0px;
|
||||
width: 300px;
|
||||
height:64px;
|
||||
background: transparent url("assets/menu_head.png") no-repeat 0 0;
|
||||
background-position: 0 0;
|
||||
background-size: 100%
|
||||
}
|
||||
.weixin-title{
|
||||
color:#fff;
|
||||
font-size:14px;
|
||||
width:100%;
|
||||
text-align: center;
|
||||
position:absolute;
|
||||
top: 33px;
|
||||
left: 0px;
|
||||
}
|
||||
.weixin-menu{
|
||||
background: transparent url("assets/menu_foot.png") no-repeat 0 0;
|
||||
padding-left: 43px;
|
||||
font-size: 12px
|
||||
}
|
||||
.menu_option{
|
||||
width: 40%!important;
|
||||
}
|
||||
.public-account-management{
|
||||
min-width: 1200px;
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
.left{
|
||||
float: left;
|
||||
display: inline-block;
|
||||
width: 350px;
|
||||
height: 715px;
|
||||
background: url("assets/iphone_backImg.png") no-repeat;
|
||||
background-size: 100% auto;
|
||||
padding: 518px 25px 88px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
/*第一级菜单*/
|
||||
.menu_main{
|
||||
.menu_bottom{
|
||||
position: relative;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
width: 85.5px;
|
||||
text-align: center;
|
||||
border: 1px solid #ebedee;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
&.menu_addicon{
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
}
|
||||
.menu_item{
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
&.active{
|
||||
border: 1px solid #2bb673;
|
||||
}
|
||||
}
|
||||
.menu_subItem{
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
&.active{
|
||||
border: 1px solid #2bb673;
|
||||
}
|
||||
}
|
||||
}
|
||||
i{
|
||||
color:#2bb673;
|
||||
}
|
||||
/*第二级菜单*/
|
||||
.submenu{
|
||||
position: absolute;
|
||||
width: 85.5px;
|
||||
bottom: 45px;
|
||||
.subtitle{
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
.save_div{
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
.save_btn{
|
||||
bottom: 20px;
|
||||
left: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*右边菜单内容*/
|
||||
.right {
|
||||
float: left;
|
||||
width: 63%;
|
||||
background-color: #e8e7e7;
|
||||
padding: 20px;
|
||||
margin-left: 20px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
.configure_page {
|
||||
.delete_btn {
|
||||
text-align: right;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.menu_content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.configur_content {
|
||||
margin-top: 20px;
|
||||
background-color: #fff;
|
||||
padding: 20px 10px;
|
||||
border-radius: 5px
|
||||
}
|
||||
.blue {
|
||||
color:#29b6f6;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.applet{
|
||||
margin-bottom: 20px;
|
||||
span{
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
.input_width {
|
||||
width: 40%;
|
||||
}
|
||||
.material{
|
||||
.input_width{
|
||||
width: 30%;
|
||||
}
|
||||
.el-textarea{
|
||||
width: 80%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-input {
|
||||
width: 70%;
|
||||
margin-right: 2%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!--素材样式-->
|
||||
<style lang="scss" scoped>
|
||||
.pagination {
|
||||
text-align: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
.select-item {
|
||||
width: 280px;
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.select-item2 {
|
||||
padding: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
.ope-row {
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.item-name {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
237
yudao-ui-admin/src/views/mp/message/index.vue
Normal file
237
yudao-ui-admin/src/views/mp/message/index.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- TODO 等待处理 -->
|
||||
<el-form-item label="消息类型" prop="type">
|
||||
<el-select v-model="queryParams.type" placeholder="请选择消息类型" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户标识" prop="openid">
|
||||
<el-input v-model="queryParams.openid" placeholder="请输入用户标识" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="发送时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="消息类型" align="center" prop="type" width="80"/>
|
||||
<el-table-column label="发送方" align="center" prop="sendFrom" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.sendFrom === 1" type="success">粉丝</el-tag>
|
||||
<el-tag v-else type="info">公众号</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="用户标识" align="center" prop="openid" width="300" />
|
||||
<el-table-column label="内容" prop="content">
|
||||
<template slot-scope="scope">
|
||||
<!-- 【事件】区域 -->
|
||||
<div v-if="scope.row.type === 'event' && scope.row.event === 'subscribe'">
|
||||
<el-tag type="success" size="mini">关注</el-tag>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'unsubscribe'">
|
||||
<el-tag type="danger" size="mini">取消关注</el-tag>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'CLICK'">
|
||||
<el-tag size="mini">点击菜单</el-tag>【{{ scope.row.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'VIEW'">
|
||||
<el-tag size="mini">点击菜单链接</el-tag>【{{ scope.row.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_waitmsg'">
|
||||
<el-tag size="mini">扫码结果</el-tag>【{{ scope.row.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_push'">
|
||||
<el-tag size="mini">扫码结果</el-tag>【{{ scope.row.eventKey }}】
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_sysphoto'">
|
||||
<el-tag size="mini">系统拍照发图</el-tag>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_photo_or_album'">
|
||||
<el-tag size="mini">拍照或者相册</el-tag>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_weixin'">
|
||||
<el-tag size="mini">微信相册</el-tag>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'location_select'">
|
||||
<el-tag size="mini">选择地理位置</el-tag>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'event'">
|
||||
<el-tag type="danger" size="mini">未知事件类型</el-tag>
|
||||
</div>
|
||||
<!-- 【消息】区域 -->
|
||||
<div v-else-if="scope.row.type === 'text'">{{ scope.row.content }}</div>
|
||||
<div v-else-if="scope.row.type === 'voice'">
|
||||
<wx-voice-player :url="scope.row.mediaUrl" :content="scope.row.recognition" />
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'image'">
|
||||
<a target="_blank" :href="scope.row.mediaUrl">
|
||||
<img :src="scope.row.mediaUrl" style="width: 100px">
|
||||
</a>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'video' || scope.row.type === 'shortvideo'">
|
||||
<wx-video-player :url="scope.row.mediaUrl" style="margin-top: 10px" />
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'link'">
|
||||
<el-tag size="mini">链接</el-tag>:
|
||||
<a :href="scope.row.url" target="_blank">{{scope.row.title}}</a>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'location'">
|
||||
<wx-location :label="scope.row.label" :location-y="scope.row.locationY" :location-x="scope.row.locationX" />
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'music'">
|
||||
<wx-music :title="scope.row.title" :description="scope.row.description" :thumb-media-url="scope.row.thumbMediaUrl"
|
||||
:music-url="scope.row.musicUrl" :hq-music-url="scope.row.hqMusicUrl" />
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'news'">
|
||||
<wx-news :articles="scope.row.articles" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-tag type="danger" size="mini">未知消息类型</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleSend(scope.row)"
|
||||
v-hasPermi="['mp:message:send']">消息
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 发送消息的弹窗 -->
|
||||
<el-dialog title="粉丝消息列表" :visible.sync="open" width="50%">
|
||||
<wx-msg :user-id="userId" v-if="open" />
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
|
||||
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
|
||||
import WxMsg from '@/views/mp/components/wx-msg/main.vue';
|
||||
import WxLocation from '@/views/mp/components/wx-location/main.vue';
|
||||
import WxMusic from '@/views/mp/components/wx-music/main.vue';
|
||||
import WxNews from '@/views/mp/components/wx-news/main.vue';
|
||||
import { getMessagePage } from "@/api/mp/message";
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
|
||||
export default {
|
||||
name: "MpMessage",
|
||||
components: {
|
||||
WxVideoPlayer,
|
||||
WxVoicePlayer,
|
||||
WxMsg,
|
||||
WxLocation,
|
||||
WxMusic,
|
||||
WxNews
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 粉丝消息列表
|
||||
list: [],
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
openid: null,
|
||||
accountId: null,
|
||||
type: null,
|
||||
createTime: []
|
||||
},
|
||||
// 操作的用户编号
|
||||
userId: 0,
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.queryParams.accountId) {
|
||||
this.$message.error('未选中公众号,无法查询消息')
|
||||
return false
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getMessagePage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
this.handleQuery();
|
||||
},
|
||||
handleSend(row) {
|
||||
this.userId = row.userId;
|
||||
this.open = true;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
362
yudao-ui-admin/src/views/mp/statistics/index.vue
Normal file
362
yudao-ui-admin/src/views/mp/statistics/index.vue
Normal file
@@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form ref="queryForm" size="small" :inline="true" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="accountId" @change="getSummary">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间范围" prop="date">
|
||||
<el-date-picker v-model="date" style="width: 260px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']"
|
||||
@change="getSummary">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 图表 -->
|
||||
<el-row>
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>用户增减数据</span>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<div ref="userSummaryChart" style="height: 420px" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>累计用户数据</span>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<div ref="userCumulateChart" style="height: 420px" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>消息概况数据</span>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<div ref="upstreamMessageChart" style="height: 420px" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>接口分析数据</span>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<div ref="interfaceSummaryChart" style="height: 420px" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 引入基本模板
|
||||
import * as echarts from 'echarts'
|
||||
// 引入柱状图组件
|
||||
require('echarts/lib/chart/bar')
|
||||
// 引入柱拆线组件
|
||||
require('echarts/lib/chart/line')
|
||||
// 引入提示框和title组件
|
||||
require('echarts/lib/component/tooltip')
|
||||
require('echarts/lib/component/title')
|
||||
require('echarts/lib/component/legend')
|
||||
|
||||
import { getInterfaceSummary, getUserSummary, getUserCumulate, getUpstreamMessage} from '@/api/mp/statistics'
|
||||
import { datePickerOptions } from "@/utils/constants";
|
||||
import {addTime, beginOfDay, betweenDay, endOfDay, formatDate} from "@/utils/dateUtils";
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
|
||||
export default {
|
||||
name: 'mpStatistics',
|
||||
data() {
|
||||
return {
|
||||
date : [beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7)), // -7 天
|
||||
endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24))], // -1 天
|
||||
accountId: undefined,
|
||||
accounts: [],
|
||||
|
||||
xAxisDate: [], // X 轴的日期范围
|
||||
userSummaryOption: { // 用户增减数据
|
||||
color: ['#67C23A', '#e5323e'],
|
||||
legend: {
|
||||
data: ['新增用户','取消关注的用户']
|
||||
},
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
data: [] // X 轴的日期范围
|
||||
},
|
||||
yAxis: {
|
||||
minInterval: 1
|
||||
},
|
||||
series: [{
|
||||
name: '新增用户',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
barGap: 0,
|
||||
data: [] // 新增用户的数据
|
||||
}, {
|
||||
name: '取消关注的用户',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [] // 取消关注的用户的数据
|
||||
}]
|
||||
},
|
||||
userCumulateOption: { // 累计用户数据
|
||||
legend: {
|
||||
data: ['累计用户量']
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: []
|
||||
},
|
||||
yAxis: {
|
||||
minInterval: 1
|
||||
},
|
||||
series: [{
|
||||
name:'累计用户量',
|
||||
data: [], // 累计用户量的数据
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
upstreamMessageOption: { // 消息发送概况数据
|
||||
color: ['#67C23A', '#e5323e'],
|
||||
legend: {
|
||||
data: ['用户发送人数', '用户发送条数']
|
||||
},
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
data: [] // X 轴的日期范围
|
||||
},
|
||||
yAxis: {
|
||||
minInterval: 1
|
||||
},
|
||||
series: [{
|
||||
name: '用户发送人数',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [] // 用户发送人数的数据
|
||||
}, {
|
||||
name: '用户发送条数',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [] // 用户发送条数的数据
|
||||
}]
|
||||
},
|
||||
interfaceSummaryOption: { // 接口分析况数据
|
||||
color: ['#67C23A', '#e5323e', '#E6A23C', '#409EFF'],
|
||||
legend: {
|
||||
data: ['被动回复用户消息的次数','失败次数', '最大耗时','总耗时']
|
||||
},
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
data: [] // X 轴的日期范围
|
||||
},
|
||||
yAxis: {},
|
||||
series: [{
|
||||
name: '被动回复用户消息的次数',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
barGap: 0,
|
||||
data: [] // 被动回复用户消息的次数的数据
|
||||
}, {
|
||||
name: '失败次数',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [] // 失败次数的数据
|
||||
}, {
|
||||
name: '最大耗时',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [] // 最大耗时的数据
|
||||
}, {
|
||||
name: '总耗时',
|
||||
type: 'bar',
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
data: [] // 总耗时的数据
|
||||
}]
|
||||
},
|
||||
|
||||
// 静态变量
|
||||
datePickerOptions: datePickerOptions,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.accountId = this.accounts[0].id;
|
||||
}
|
||||
// 加载数据
|
||||
this.getSummary();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getSummary() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.accountId) {
|
||||
this.$message.error('未选中公众号,无法统计数据')
|
||||
return false
|
||||
}
|
||||
// 必须选择 7 天内,因为公众号有时间跨度限制为 7
|
||||
if (betweenDay(this.date[0], this.date[1]) >= 7) {
|
||||
this.$message.error('时间间隔 7 天以内,请重新选择')
|
||||
return false
|
||||
}
|
||||
this.xAxisDate = []
|
||||
const days = betweenDay(this.date[0], this.date[1]) // 相差天数
|
||||
for(let i = 0; i <= days; i++){
|
||||
this.xAxisDate.push(formatDate(addTime(this.date[0], 3600 * 1000 * 24 * i), 'yyyy-MM-dd'));
|
||||
}
|
||||
|
||||
// 初始化图表
|
||||
this.initUserSummaryChart();
|
||||
this.initUserCumulateChart();
|
||||
this.initUpstreamMessageChart();
|
||||
this.interfaceSummaryChart();
|
||||
},
|
||||
initUserSummaryChart() {
|
||||
this.userSummaryOption.xAxis.data = [];
|
||||
this.userSummaryOption.series[0].data = [];
|
||||
this.userSummaryOption.series[1].data = [];
|
||||
getUserSummary({
|
||||
accountId: this.accountId,
|
||||
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
|
||||
}).then(response => {
|
||||
this.userSummaryOption.xAxis.data = this.xAxisDate;
|
||||
// 处理数据
|
||||
this.xAxisDate.forEach((date, index) => {
|
||||
response.data.forEach((item) => {
|
||||
// 匹配日期
|
||||
const refDate = formatDate(new Date(item.refDate), 'yyyy-MM-dd');
|
||||
if (refDate.indexOf(date) === -1) {
|
||||
return;
|
||||
}
|
||||
// 设置数据到对应的位置
|
||||
this.userSummaryOption.series[0].data[index] = item.newUser;
|
||||
this.userSummaryOption.series[1].data[index] = item.cancelUser;
|
||||
})
|
||||
})
|
||||
// 绘制图表
|
||||
const userSummaryChart = echarts.init(this.$refs.userSummaryChart);
|
||||
userSummaryChart.setOption(this.userSummaryOption)
|
||||
}).catch(() => {})
|
||||
},
|
||||
initUserCumulateChart() {
|
||||
this.userCumulateOption.xAxis.data = [];
|
||||
this.userCumulateOption.series[0].data = [];
|
||||
// 发起请求
|
||||
getUserCumulate({
|
||||
accountId: this.accountId,
|
||||
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
|
||||
}).then(response => {
|
||||
this.userCumulateOption.xAxis.data = this.xAxisDate;
|
||||
// 处理数据
|
||||
response.data.forEach((item, index) => {
|
||||
this.userCumulateOption.series[0].data[index] = item.cumulateUser;
|
||||
})
|
||||
// 绘制图表
|
||||
const userCumulateChart = echarts.init(this.$refs.userCumulateChart);
|
||||
userCumulateChart.setOption(this.userCumulateOption)
|
||||
}).catch(() => {})
|
||||
},
|
||||
initUpstreamMessageChart() {
|
||||
this.upstreamMessageOption.xAxis.data = [];
|
||||
this.upstreamMessageOption.series[0].data = [];
|
||||
this.upstreamMessageOption.series[1].data = [];
|
||||
// 发起请求
|
||||
getUpstreamMessage({
|
||||
accountId: this.accountId,
|
||||
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
|
||||
}).then(response => {
|
||||
this.upstreamMessageOption.xAxis.data = this.xAxisDate;
|
||||
// 处理数据
|
||||
response.data.forEach((item, index) => {
|
||||
this.upstreamMessageOption.series[0].data[index] = item.messageUser;
|
||||
this.upstreamMessageOption.series[1].data[index] = item.messageCount;
|
||||
})
|
||||
// 绘制图表
|
||||
const upstreamMessageChart = echarts.init(this.$refs.upstreamMessageChart);
|
||||
upstreamMessageChart.setOption(this.upstreamMessageOption);
|
||||
}).catch(() => {})
|
||||
},
|
||||
interfaceSummaryChart() {
|
||||
this.interfaceSummaryOption.xAxis.data = [];
|
||||
this.interfaceSummaryOption.series[0].data = [];
|
||||
this.interfaceSummaryOption.series[1].data = [];
|
||||
this.interfaceSummaryOption.series[2].data = [];
|
||||
this.interfaceSummaryOption.series[3].data = [];
|
||||
// 发起请求
|
||||
getInterfaceSummary({
|
||||
accountId: this.accountId,
|
||||
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
|
||||
}).then(response => {
|
||||
this.interfaceSummaryOption.xAxis.data = this.xAxisDate;
|
||||
// 处理数据
|
||||
response.data.forEach((item, index) => {
|
||||
this.interfaceSummaryOption.series[0].data[index] = item.callbackCount;
|
||||
this.interfaceSummaryOption.series[1].data[index] = item.failCount;
|
||||
this.interfaceSummaryOption.series[2].data[index] = item.maxTimeCost;
|
||||
this.interfaceSummaryOption.series[3].data[index] = item.totalTimeCost;
|
||||
})
|
||||
// 绘制图表
|
||||
const interfaceSummaryChart = echarts.init(this.$refs.interfaceSummaryChart);
|
||||
interfaceSummaryChart.setOption(this.interfaceSummaryOption);
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
241
yudao-ui-admin/src/views/mp/tag/index.vue
Normal file
241
yudao-ui-admin/src/views/mp/tag/index.vue
Normal file
@@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="标签名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入标签名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['mp:tag:create']">新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="el-icon-refresh" size="mini" @click="handleSync"
|
||||
v-hasPermi="['mp:tag:sync']">同步
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="id"/>
|
||||
<el-table-column label="标签名称" align="center" prop="name"/>
|
||||
<el-table-column label="粉丝数" align="center" prop="count"/>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['mp:tag:update']">修改
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['mp:tag:delete']">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="标签名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入标签名称"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createTag,
|
||||
updateTag,
|
||||
deleteTag,
|
||||
getTag,
|
||||
getTagPage,
|
||||
syncTag,
|
||||
} from '@/api/mp/tag'
|
||||
import { getSimpleAccounts} from '@/api/mp/account'
|
||||
|
||||
export default {
|
||||
name: 'MpTag',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 公众号标签列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
accountId: null,
|
||||
name: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
accountId: undefined,
|
||||
name: undefined,
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{ required: true, message: '请输入标签名称', trigger: 'blur' }]
|
||||
},
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.queryParams.accountId) {
|
||||
this.$message.error('未选中公众号,无法查询标签')
|
||||
return false
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams}
|
||||
// 执行查询
|
||||
getTagPage(params).then(response => {
|
||||
this.list = response.data.list
|
||||
this.total = response.data.total
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false
|
||||
this.reset()
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
accountId: undefined,
|
||||
name: undefined,
|
||||
}
|
||||
this.resetForm('form')
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.open = true
|
||||
this.title = '添加公众号标签'
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const id = row.id
|
||||
getTag(id).then(response => {
|
||||
this.form = response.data
|
||||
this.open = true
|
||||
this.title = '修改公众号标签'
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
this.form.accountId = this.queryParams.accountId;
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateTag(this.form).then(response => {
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
return
|
||||
}
|
||||
// 添加的提交
|
||||
createTag(this.form).then(response => {
|
||||
this.$modal.msgSuccess('新增成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
})
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id
|
||||
this.$modal.confirm('是否确认删除公众号标签编号为"' + id + '"的数据项?').then(function () {
|
||||
return deleteTag(id)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
/** 同步标签 */
|
||||
handleSync() {
|
||||
const accountId = this.queryParams.accountId
|
||||
this.$modal.confirm('是否确认同步标签?').then(function () {
|
||||
return syncTag(accountId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('同步标签成功')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
235
yudao-ui-admin/src/views/mp/user/index.vue
Normal file
235
yudao-ui-admin/src/views/mp/user/index.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="公众号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
|
||||
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户标识" prop="openid">
|
||||
<el-input v-model="queryParams.openid" placeholder="请输入用户标识" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" prop="nickname">
|
||||
<el-input v-model="queryParams.nickname" placeholder="请输入昵称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="el-icon-refresh" size="mini" @click="handleSync"
|
||||
v-hasPermi="['mp:user:sync']">同步
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="用户标识" align="center" prop="openid" width="260" />
|
||||
<el-table-column label="昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="标签" align="center" prop="tagIds" width="200">
|
||||
<template slot-scope="scope">
|
||||
<span v-for="(tagId, index) in scope.row.tagIds" :key="index">
|
||||
<el-tag>{{ tags.find(tag => tag.tagId === tagId)?.name }} </el-tag>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订阅状态" align="center" prop="subscribeStatus">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.subscribeStatus === 0" type="success">已订阅</el-tag>
|
||||
<el-tag v-else type="danger">未订阅</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订阅时间" align="center" prop="subscribeTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.subscribeTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['mp:user:update']">修改</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="昵称" prop="nickname">
|
||||
<el-input v-model="form.nickname" placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标签" prop="tagIds">
|
||||
<el-select v-model="form.tagIds" multiple clearable placeholder="请选择标签">
|
||||
<el-option v-for="item in tags" :key="parseInt(item.tagId)" :label="item.name" :value="parseInt(item.tagId)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { updateUser, getUser, getUserPage, syncUser } from "@/api/mp/user";
|
||||
import { getSimpleAccounts } from "@/api/mp/account";
|
||||
import { getSimpleTags } from "@/api/mp/tag";
|
||||
|
||||
export default {
|
||||
name: "WxAccountFans",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 微信公众号粉丝列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
accountId: null,
|
||||
openid: null,
|
||||
nickname: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {},
|
||||
|
||||
// 公众号账号列表
|
||||
accounts: [],
|
||||
// 公众号标签列表
|
||||
tags: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
getSimpleAccounts().then(response => {
|
||||
this.accounts = response.data;
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
// 加载数据
|
||||
this.getList();
|
||||
})
|
||||
|
||||
// 加载标签
|
||||
getSimpleTags().then(response => {
|
||||
this.tags = response.data;
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
// 如果没有选中公众号账号,则进行提示。
|
||||
if (!this.queryParams.accountId) {
|
||||
this.$message.error('未选中公众号,无法查询用户')
|
||||
return false
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
// 执行查询
|
||||
getUserPage(params).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
nickname: undefined,
|
||||
remark: undefined,
|
||||
tagIds: [],
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
// 默认选中第一个
|
||||
if (this.accounts.length > 0) {
|
||||
this.queryParams.accountId = this.accounts[0].id;
|
||||
}
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getUser(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改公众号粉丝";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateUser(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 同步标签 */
|
||||
handleSync() {
|
||||
const accountId = this.queryParams.accountId
|
||||
this.$modal.confirm('是否确认同步粉丝?').then(function () {
|
||||
return syncUser(accountId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('开始从微信公众号同步粉丝信息,同步需要一段时间,建议稍后再查询')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -47,20 +47,20 @@
|
||||
<el-table-column label="应用编号" align="center" prop="id"/>
|
||||
<el-table-column label="应用名" align="center" prop="name"/>
|
||||
<el-table-column label="开启状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1"
|
||||
@change="handleStatusChange(scope.row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商户名称" align="center" prop="payMerchant.name"/>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付宝配置" align="center">
|
||||
<el-table-column :label="payChannelEnum.ALIPAY_APP.name" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="success" icon="el-icon-check" circle
|
||||
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_APP.code)"
|
||||
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_APP.code,payType.ALIPAY)">
|
||||
@@ -72,7 +72,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="payChannelEnum.ALIPAY_PC.name" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="success" icon="el-icon-check" circle
|
||||
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_PC.code)"
|
||||
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_PC.code,payType.ALIPAY)">
|
||||
@@ -84,7 +84,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="payChannelEnum.ALIPAY_WAP.name" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="success" icon="el-icon-check" circle
|
||||
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_WAP.code)"
|
||||
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_WAP.code,payType.ALIPAY)">
|
||||
@@ -96,7 +96,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="payChannelEnum.ALIPAY_QR.name" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="success" icon="el-icon-check" circle
|
||||
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_QR.code)"
|
||||
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_QR.code,payType.ALIPAY)">
|
||||
@@ -110,7 +110,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="微信配置" align="center">
|
||||
<el-table-column :label="payChannelEnum.WX_LITE.name" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="success" icon="el-icon-check" circle
|
||||
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_LITE.code)"
|
||||
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_LITE.code,payType.WECHAT)">
|
||||
@@ -122,7 +122,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="payChannelEnum.WX_PUB.name" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="success" icon="el-icon-check" circle
|
||||
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_PUB.code)"
|
||||
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_PUB.code,payType.WECHAT)">
|
||||
@@ -134,7 +134,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="payChannelEnum.WX_APP.name" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button type="success" icon="el-icon-check" circle
|
||||
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_APP.code)"
|
||||
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_APP.code,payType.WECHAT)">
|
||||
@@ -147,7 +147,7 @@
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['pay:app:update']">修改
|
||||
</el-button>
|
||||
|
||||
@@ -50,18 +50,18 @@
|
||||
<el-table-column label="商户全称" align="center" prop="name" />
|
||||
<el-table-column label="商户简称" align="center" prop="shortName" />
|
||||
<el-table-column label="开启状态" align="center" prop="status" >
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['pay:merchant:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
|
||||
@@ -94,48 +94,48 @@
|
||||
<!-- <el-table-column label="渠道订单号" align="center" prop="channelOrderNo" width="140"/>-->
|
||||
<el-table-column label="商品标题" align="center" prop="subject" width="180" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="支付金额" align="center" prop="amount" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
¥{{ parseFloat(scope.row.amount / 100).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手续金额" align="center" prop="channelFeeAmount" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
¥{{ parseFloat(scope.row.channelFeeAmount / 100).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款金额" align="center" prop="refundAmount" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
¥{{ parseFloat(scope.row.refundAmount / 100).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="退款状态" align="center" prop="refundStatus">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <template v-slot="scope">-->
|
||||
<!-- <span>{{ getDictDataLabel(DICT_TYPE.PAY_ORDER_REFUND_STATUS, scope.row.refundStatus) }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="回调状态" align="center" prop="notifyStatus" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付时间" align="center" prop="successTime" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.successTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-search" @click="handleQueryDetails(scope.row)"
|
||||
v-hasPermi="['pay:order:query']">查看详情
|
||||
</el-button>
|
||||
@@ -219,7 +219,6 @@ import {getOrder, getOrderPage, exportOrderExcel} from "@/api/pay/order";
|
||||
import {getMerchantListByName} from "@/api/pay/merchant";
|
||||
import {getAppListByMerchantId} from "@/api/pay/app";
|
||||
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
|
||||
import {PayOrderNotifyStatusEnum, PayOrderRefundStatusEnum, PayOrderStatusEnum} from "@/utils/constants";
|
||||
import { getNowDateTime} from "@/utils/ruoyi";
|
||||
|
||||
const defaultOrderDetail = {
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<!-- <el-table-column label="商户名称" align="center" prop="merchantName" width="120"/>-->
|
||||
<!-- <el-table-column label="应用名称" align="center" prop="appName" width="120"/>-->
|
||||
<el-table-column label="支付渠道" align="center" width="130">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-popover trigger="hover" placement="top">
|
||||
<p>商户名称: {{ scope.row.merchantName }}</p>
|
||||
<p>应用名称: {{ scope.row.appName }}</p>
|
||||
@@ -82,7 +82,7 @@
|
||||
<!-- <el-table-column label="交易订单号" align="center" prop="tradeNo" width="140"/>-->
|
||||
<!-- <el-table-column label="商户订单编号" align="center" prop="merchantOrderId" width="140"/>-->
|
||||
<el-table-column label="商户订单号" align="left" width="230">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<p class="order-font">
|
||||
<el-tag size="mini">退款</el-tag>
|
||||
{{ scope.row.merchantRefundNo }}
|
||||
@@ -94,7 +94,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付订单号" align="center" prop="merchantRefundNo" width="250">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<p class="order-font">
|
||||
<el-tag size="mini">交易</el-tag>
|
||||
{{ scope.row.tradeNo }}
|
||||
@@ -106,43 +106,43 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付金额(元)" align="center" prop="payAmount" width="100">
|
||||
<template slot-scope="scope" class="">
|
||||
<template v-slot="scope" class="">
|
||||
¥{{ parseFloat(scope.row.payAmount / 100).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款金额(元)" align="center" prop="refundAmount" width="100">
|
||||
<template scope="scope">
|
||||
<template v-slot="scope">
|
||||
¥{{ parseFloat(scope.row.refundAmount / 100).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款类型" align="center" prop="type" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回调状态" align="center" prop="notifyStatus">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款原因" align="center" prop="reason" width="140" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款成功时间" align="center" prop="successTime" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.successTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-search" @click="handleQueryDetails(scope.row)"
|
||||
v-hasPermi="['pay:order:query']">查看详情
|
||||
</el-button>
|
||||
@@ -177,7 +177,7 @@
|
||||
<el-tag class="tag-purple" size="mini">{{ parseFloat(refundDetail.refundAmount / 100).toFixed(2) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="退款类型">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="refundDetail.type" />
|
||||
</template>
|
||||
</el-descriptions-item>
|
||||
|
||||
@@ -32,20 +32,11 @@
|
||||
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon"/>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" v-if="captchaEnable">
|
||||
<el-input v-model="loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%"
|
||||
@keyup.enter.native="handleLogin">
|
||||
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon"/>
|
||||
</el-input>
|
||||
<div class="login-code">
|
||||
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-checkbox v-model="loginForm.rememberMe" style="margin:0 0 25px 0;">记住密码</el-checkbox>
|
||||
<!-- 下方的登录按钮 -->
|
||||
<el-form-item style="width:100%;">
|
||||
<el-button :loading="loading" size="medium" type="primary" style="width:100%;"
|
||||
@click.native.prevent="handleLogin">
|
||||
@click.native.prevent="getCode">
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
</el-button>
|
||||
@@ -55,6 +46,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图形验证码 -->
|
||||
<Verify ref="verify" :captcha-type="'blockPuzzle'" :img-size="{width:'400px',height:'200px'}"
|
||||
@success="handleLogin" />
|
||||
|
||||
<!-- footer -->
|
||||
<div class="footer">
|
||||
Copyright © 2020-2022 iocoder.cn All Rights Reserved.
|
||||
@@ -72,10 +68,15 @@ import {
|
||||
setRememberMe,
|
||||
setUsername
|
||||
} from "@/utils/auth";
|
||||
import {getCodeImg} from "@/api/login";
|
||||
|
||||
import Verify from '@/components/Verifition/Verify';
|
||||
import {getCaptchaEnable} from "@/utils/ruoyi";
|
||||
|
||||
export default {
|
||||
name: "ThirdLogin",
|
||||
components: {
|
||||
Verify
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
codeUrl: "",
|
||||
@@ -85,8 +86,7 @@ export default {
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
rememberMe: false,
|
||||
code: "",
|
||||
uuid: "",
|
||||
captchaVerification: "",
|
||||
},
|
||||
loginRules: {
|
||||
username: [
|
||||
@@ -104,23 +104,19 @@ export default {
|
||||
state: undefined,
|
||||
};
|
||||
},
|
||||
// watch: {
|
||||
// $route: {
|
||||
// handler: function(route) {
|
||||
// this.redirect = route.query && route.query.redirect;
|
||||
// },
|
||||
// immediate: true
|
||||
// }
|
||||
// },
|
||||
created() {
|
||||
this.getCookie();
|
||||
// 验证码开关
|
||||
this.captchaEnable = getCaptchaEnable();
|
||||
// 重定向地址
|
||||
this.redirect = this.$route.query.redirect;
|
||||
this.getCode();
|
||||
this.redirect = this.getUrlValue('redirect');
|
||||
// 社交登录相关
|
||||
this.type = this.$route.query.type;
|
||||
this.type = this.getUrlValue('type');
|
||||
this.code = this.$route.query.code;
|
||||
this.state = this.$route.query.state;
|
||||
|
||||
// 尝试登录一下
|
||||
this.loading = true;
|
||||
this.$store.dispatch("SocialLogin", {
|
||||
code: this.code,
|
||||
state: this.state,
|
||||
@@ -133,19 +129,15 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getCode() {
|
||||
// 只有开启的状态,才加载验证码。默认开启
|
||||
// 情况一,未开启:则直接登录
|
||||
if (!this.captchaEnable) {
|
||||
this.handleLogin({})
|
||||
return;
|
||||
}
|
||||
// 请求远程,获得验证码
|
||||
getCodeImg().then(res => {
|
||||
res = res.data;
|
||||
this.captchaEnable = res.enable;
|
||||
if (this.captchaEnable) {
|
||||
this.codeUrl = "data:image/gif;base64," + res.img;
|
||||
this.loginForm.uuid = res.uuid;
|
||||
}
|
||||
});
|
||||
|
||||
// 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录
|
||||
// 弹出验证码
|
||||
this.$refs.verify.show()
|
||||
},
|
||||
getCookie() {
|
||||
const username = getUsername();
|
||||
@@ -158,7 +150,7 @@ export default {
|
||||
loginType: this.loginForm.loginType,
|
||||
};
|
||||
},
|
||||
handleLogin() {
|
||||
handleLogin(captchaParams) {
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
@@ -177,8 +169,7 @@ export default {
|
||||
// 账号密码登录
|
||||
username: this.loginForm.username,
|
||||
password: this.loginForm.password,
|
||||
code: this.loginForm.code,
|
||||
uuid: this.loginForm.uuid,
|
||||
captchaVerification: captchaParams.captchaVerification
|
||||
}).then(() => {
|
||||
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
|
||||
}).catch(() => {
|
||||
@@ -187,6 +178,10 @@ export default {
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
getUrlValue(key) {
|
||||
const url = new URL(decodeURIComponent(location.href));
|
||||
return url.searchParams.get(key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,12 +19,7 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div>
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="LoginRules" class="login-form">
|
||||
<el-form-item prop="tenantName" v-if="tenantEnable">
|
||||
<el-input v-model="loginForm.tenantName" type="text" auto-complete="off" placeholder='租户'>
|
||||
<svg-icon slot="prefix" icon-class="tree" class="el-input__icon input-icon"/>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form ref="loginForm" :model="loginForm" class="login-form">
|
||||
<!-- 授权范围的选择 -->
|
||||
此第三方应用请求获得以下权限:
|
||||
<el-form-item prop="scopes">
|
||||
@@ -56,18 +51,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getTenantIdByName} from "@/api/system/tenant";
|
||||
import {getTenantEnable} from "@/utils/ruoyi";
|
||||
import {authorize, getAuthorize} from "@/api/login";
|
||||
import {getTenantName, setTenantId} from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
return {
|
||||
tenantEnable: true,
|
||||
loginForm: {
|
||||
tenantName: "芋道源码",
|
||||
scopes: [], // 已选中的 scope 数组
|
||||
},
|
||||
params: { // URL 上的 client_id、scope 等参数
|
||||
@@ -81,35 +71,10 @@ export default {
|
||||
name: '',
|
||||
logo: '',
|
||||
},
|
||||
LoginRules: {
|
||||
tenantName: [
|
||||
{required: true, trigger: "blur", message: "租户不能为空"},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// debugger
|
||||
getTenantIdByName(value).then(res => {
|
||||
const tenantId = res.data;
|
||||
if (tenantId && tenantId >= 0) {
|
||||
// 设置租户
|
||||
setTenantId(tenantId)
|
||||
callback();
|
||||
} else {
|
||||
callback('租户不存在');
|
||||
}
|
||||
});
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 租户开关
|
||||
this.tenantEnable = getTenantEnable();
|
||||
this.getCookie();
|
||||
|
||||
// 解析参数
|
||||
// 例如说【自动授权不通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read%20user.write
|
||||
// 例如说【自动授权通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read
|
||||
@@ -162,13 +127,6 @@ export default {
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getCookie() {
|
||||
const tenantName = getTenantName();
|
||||
this.loginForm = {
|
||||
...this.loginForm,
|
||||
tenantName: tenantName ? tenantName : this.loginForm.tenantName,
|
||||
};
|
||||
},
|
||||
handleAuthorize(approved) {
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (!valid) {
|
||||
@@ -217,7 +175,7 @@ export default {
|
||||
@import "~@/assets/styles/login.scss";
|
||||
.oauth-login {
|
||||
display: flex;
|
||||
align-items: cen;
|
||||
align-items: center;
|
||||
cursor:pointer;
|
||||
}
|
||||
.oauth-login-item {
|
||||
|
||||
114
yudao-ui-admin/src/views/system/area/index.vue
Normal file
114
yudao-ui-admin/src/views/system/area/index.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<doc-alert title="地区 & IP" url="https://doc.iocoder.cn/area-and-ip/" />
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">IP 查询
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
|
||||
<el-table-column label="编号" prop="id"/>
|
||||
<el-table-column label="名字" prop="name"/>
|
||||
</el-table>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog title="IP 查询" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="IP" prop="ip">
|
||||
<el-input v-model="form.ip" placeholder="请输入 IP 地址"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="result">
|
||||
<el-input v-model="form.result" readonly placeholder="展示查询 IP 结果" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">查 询</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getAreaByIp, getAreaTree} from "@/api/system/area";
|
||||
|
||||
export default {
|
||||
name: "Area",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 地区列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 重新渲染表格状态
|
||||
refreshTable: true,
|
||||
// 表单参数
|
||||
form: {
|
||||
ip: undefined,
|
||||
result: undefined,
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
ip: [{required: true, message: "IP 地址不能为控", trigger: "blur"}],
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
getAreaTree().then(response => {
|
||||
this.list = response.data;
|
||||
this.loading = false;
|
||||
})
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
ip: undefined,
|
||||
result: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
getAreaByIp(this.form.ip).then(response => {
|
||||
this.$modal.msgSuccess("查询成功");
|
||||
this.form.result = response.data
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -32,17 +32,17 @@
|
||||
<el-table-column prop="leader" label="负责人" :formatter="userNicknameFormat" width="120"/>
|
||||
<el-table-column prop="sort" label="排序" width="200"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dept:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-plus" @click="handleAdd(scope.row)"
|
||||
@@ -141,8 +141,6 @@ export default {
|
||||
refreshTable: true,
|
||||
// 是否展开
|
||||
expand: false,
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
name: undefined,
|
||||
@@ -167,7 +165,7 @@ export default {
|
||||
],
|
||||
phone: [
|
||||
{
|
||||
pattern: /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/,
|
||||
pattern: /^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/,
|
||||
message: "请输入正确的手机号码",
|
||||
trigger: "blur"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="数据状态" clearable>
|
||||
<el-option v-for="dict in statusOptions" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
<el-option v-for="dict in statusDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -38,7 +38,7 @@
|
||||
<el-table-column label="字典键值" align="center" prop="value" />
|
||||
<el-table-column label="字典排序" align="center" prop="sort" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -46,12 +46,12 @@
|
||||
<el-table-column label="CSS Class" align="center" prop="cssClass" />
|
||||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dict:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
@@ -130,8 +130,6 @@ export default {
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 类型数据字典
|
||||
typeOptions: [],
|
||||
// 查询参数
|
||||
|
||||
@@ -38,25 +38,25 @@
|
||||
<el-table-column label="字典编号" align="center" prop="id" />
|
||||
<el-table-column label="字典名称" align="center" prop="name" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<router-link :to="'/dict/type/data/' + scope.row.id" class="link-type">
|
||||
<span>{{ scope.row.type }}</span>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dict:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
@@ -118,8 +118,6 @@ export default {
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="类型" align="center" prop="type" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_ERROR_CODE_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -54,12 +54,12 @@
|
||||
<el-table-column label="错误码提示" align="center" prop="message" width="300" />
|
||||
<el-table-column label="备注" align="center" prop="memo" width="200" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:error-code:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="访问编号" align="center" prop="id" />
|
||||
<el-table-column label="日志类型" align="center" prop="logType" width="120">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_LOGIN_TYPE" :value="scope.row.logType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -46,12 +46,12 @@
|
||||
<el-table-column label="登录地址" align="center" prop="userIp" width="130" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="userAgent" align="center" prop="userAgent" width="400" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="结果" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_LOGIN_RESULT" :value="scope.row.result" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="登录日期" align="center" prop="loginTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -79,8 +79,6 @@ export default {
|
||||
total: 0,
|
||||
// 表格数据
|
||||
list: [],
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
|
||||
225
yudao-ui-admin/src/views/system/mail/account/index.vue
Executable file
225
yudao-ui-admin/src/views/system/mail/account/index.vue
Executable file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="邮箱" prop="mail">
|
||||
<el-input v-model="queryParams.mail" placeholder="请输入邮箱" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="queryParams.username" placeholder="请输入用户名" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['system:mail-account:create']">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="邮箱" align="center" prop="mail" />
|
||||
<el-table-column label="用户名" align="center" prop="username" />
|
||||
<el-table-column label="SMTP 服务器域名" align="center" prop="host" />
|
||||
<el-table-column label="SMTP 服务器端口" align="center" prop="port" />
|
||||
<el-table-column label="是否开启 SSL" align="center" prop="sslEnable">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.sslEnable" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:mail-account:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:mail-account:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
|
||||
<el-form-item label="邮箱" prop="mail">
|
||||
<el-input v-model="form.mail" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="form.username" placeholder="请输入用户名,一般和邮箱一致" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="SMTP 服务器域名" prop="host">
|
||||
<el-input v-model="form.host" placeholder="请输入 SMTP 服务器域名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="SMTP 服务器端口" prop="port">
|
||||
<el-input v-model="form.port" placeholder="请输入 SMTP 服务器端口" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否开启 SSL" prop="sslEnable">
|
||||
<el-radio-group v-model="form.sslEnable">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value" :label="dict.value === 'true'">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createMailAccount, updateMailAccount, deleteMailAccount, getMailAccount, getMailAccountPage } from "@/api/system/mail/account";
|
||||
|
||||
export default {
|
||||
name: "MailAccount",
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 邮箱账号列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
mail: null,
|
||||
username: null
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
mail: [{ required: true, message: "邮箱不能为空", trigger: "blur" }],
|
||||
username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
|
||||
password: [{ required: true, message: "密码不能为空", trigger: "blur" }],
|
||||
host: [{ required: true, message: "SMTP 服务器域名不能为空", trigger: "blur" }],
|
||||
port: [{ required: true, message: "SMTP 服务器端口不能为空", trigger: "blur" }],
|
||||
sslEnable: [{ required: true, message: "是否开启 SSL不能为空", trigger: "blur" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getMailAccountPage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
mail: undefined,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
host: undefined,
|
||||
port: undefined,
|
||||
sslEnable: true,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加邮箱账号";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getMailAccount(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改邮箱账号";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateMailAccount(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createMailAccount(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除邮箱账号编号为"' + id + '"的数据项?').then(function() {
|
||||
return deleteMailAccount(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
226
yudao-ui-admin/src/views/system/mail/log/index.vue
Executable file
226
yudao-ui-admin/src/views/system/mail/log/index.vue
Executable file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="接收邮箱" prop="toMail">
|
||||
<el-input v-model="queryParams.toMail" placeholder="请输入接收邮箱" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱账号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请输入邮箱账号" clearable>
|
||||
<el-option v-for="account in accountOptions" :key="account.id" :value="account.id" :label="account.mail" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模板编号" prop="templateId">
|
||||
<el-input v-model="queryParams.templateId" placeholder="请输入模板编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="发送状态" prop="sendStatus">
|
||||
<el-select v-model="queryParams.sendStatus" placeholder="请选择发送状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_MAIL_SEND_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="发送时间" prop="sendTime">
|
||||
<el-date-picker v-model="queryParams.sendTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户编号" prop="userId">
|
||||
<el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户类型" prop="userType">
|
||||
<el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable>
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="发送时间" align="center" prop="sendTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.sendTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="接收邮箱" align="center" prop="toMail" width="200">
|
||||
<template v-slot="scope">
|
||||
<div>{{ scope.row.toMail }}</div>
|
||||
<div v-if="scope.row.userType && scope.row.userId">
|
||||
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>{{ '(' + scope.row.userId + ')' }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="邮件标题" align="center" prop="templateTitle" />
|
||||
<el-table-column label="发送状态" align="center" prop="sendStatus">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_MAIL_SEND_STATUS" :value="scope.row.sendStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="邮箱账号" align="center" prop="fromMail" />
|
||||
<el-table-column label="模板编号" align="center" prop="templateId" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"
|
||||
v-hasPermi="['system:mail-log:query']">详细</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 邮件日志详细-->
|
||||
<el-dialog :title="title" :visible.sync="open" width="700px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" label-width="160px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="日志主键:">{{ form.id }}</el-form-item>
|
||||
<el-form-item label="用户编号:">{{ form.userId }}</el-form-item>
|
||||
<el-form-item label="用户类型:">
|
||||
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="form.userType"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="接收邮箱地址:">{{ form.toMail }}</el-form-item>
|
||||
<el-form-item label="邮箱账号编号:">{{ form.accountId }}</el-form-item>
|
||||
<el-form-item label="发送邮箱地址:">{{ form.fromMail }}</el-form-item>
|
||||
<el-form-item label="模板编号:">{{ form.templateId }}</el-form-item>
|
||||
<el-form-item label="模板编码:">{{ form.templateCode }}</el-form-item>
|
||||
<el-form-item label="模版发送人名称:">{{ form.templateNickname }}</el-form-item>
|
||||
<el-form-item label="邮件标题:">{{ form.templateTitle }}</el-form-item>
|
||||
<el-form-item label="邮件内容:">
|
||||
<editor v-model="form.templateContent" :min-height="192" read-only />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮件参数:">{{ form.templateParams }}</el-form-item>
|
||||
<el-form-item label="发送状态:">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_MAIL_SEND_STATUS" :value="form.sendStatus" />
|
||||
</el-form-item>
|
||||
<el-form-item label="发送时间:">{{ parseTime(form.sendTime) }}</el-form-item>
|
||||
<el-form-item label="发送返回的消息编号:">{{ form.sendMessageId }}</el-form-item>
|
||||
<el-form-item label="发送异常:">{{ form.sendException }}</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="open = false">关 闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMailLog, getMailLogPage } from "@/api/system/mail/log";
|
||||
import Editor from '@/components/Editor';
|
||||
import { getSimpleMailAccountList } from "@/api/system/mail/account";
|
||||
|
||||
export default {
|
||||
name: "MailLog",
|
||||
components: {
|
||||
Editor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 邮件日志列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
userId: null,
|
||||
userType: null,
|
||||
toMail: null,
|
||||
accountId: null,
|
||||
templateId: null,
|
||||
sendStatus: null,
|
||||
sendTime: [],
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 邮箱账号
|
||||
accountOptions: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
// 获得邮箱账号列表
|
||||
getSimpleMailAccountList().then(response => {
|
||||
this.accountOptions = response.data
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getMailLogPage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
userId: undefined,
|
||||
userType: undefined,
|
||||
toMail: undefined,
|
||||
accountId: undefined,
|
||||
fromMail: undefined,
|
||||
templateId: undefined,
|
||||
templateCode: undefined,
|
||||
templateNickname: undefined,
|
||||
templateTitle: undefined,
|
||||
templateContent: undefined,
|
||||
templateParams: undefined,
|
||||
sendStatus: undefined,
|
||||
sendTime: undefined,
|
||||
sendMessageId: undefined,
|
||||
sendException: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 详细按钮操作 */
|
||||
handleView(row) {
|
||||
this.open = true;
|
||||
this.form = row;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
349
yudao-ui-admin/src/views/system/mail/template/index.vue
Executable file
349
yudao-ui-admin/src/views/system/mail/template/index.vue
Executable file
@@ -0,0 +1,349 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="模板名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入模板名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模板编码" prop="code">
|
||||
<el-input v-model="queryParams.code" placeholder="请输入模板编码" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱账号" prop="accountId">
|
||||
<el-select v-model="queryParams.accountId" placeholder="请输入邮箱账号" clearable>
|
||||
<el-option v-for="account in accountOptions" :key="account.id" :value="account.id" :label="account.mail" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['system:mail-template:create']">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="模板编码" align="center" prop="code" />
|
||||
<el-table-column label="模板名称" align="center" prop="name" />
|
||||
<el-table-column label="模板标题" align="center" prop="title" />
|
||||
<el-table-column label="模板内容" align="center" prop="content" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="邮箱账号" align="center" prop="accountId" width="200">
|
||||
<template v-slot="scope">
|
||||
{{ accountOptions.find(account => account.id === scope.row.accountId)?.mail }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发送人名称" align="center" prop="nickname" />
|
||||
<el-table-column label="开启状态" align="center" prop="status">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-share" @click="handleSend(scope.row)"
|
||||
v-hasPermi="['system:mail-template:send-mail']">测试</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:mail-template:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:mail-template:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="模板名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入模板名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模板编码" prop="code">
|
||||
<el-input v-model="form.code" placeholder="请输入模板编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱账号" prop="accountId">
|
||||
<el-select v-model="form.accountId" placeholder="请输入邮箱账号">
|
||||
<el-option v-for="account in accountOptions" :key="account.id" :value="account.id" :label="account.mail" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="发送人名称" prop="nickname">
|
||||
<el-input v-model="form.nickname" placeholder="请输入发送人名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模板标题" prop="title">
|
||||
<el-input v-model="form.title" placeholder="请输入模板标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模板内容">
|
||||
<editor v-model="form.content" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 对话框(发送邮件) -->
|
||||
<el-dialog title="测试发送邮件" :visible.sync="sendOpen" width="500px" append-to-body>
|
||||
<el-form ref="sendForm" :model="sendForm" :rules="sendRules" label-width="140px">
|
||||
<el-form-item label="模板内容" prop="content">
|
||||
<editor v-model="sendForm.content" :min-height="192" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item label="收件邮箱" prop="mail">
|
||||
<el-input v-model="sendForm.mail" placeholder="请输入收件邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item v-for="param in sendForm.params" :key="param" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param">
|
||||
<el-input v-model="sendForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitSendForm">确 定</el-button>
|
||||
<el-button @click="cancelSend">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createMailTemplate, updateMailTemplate, deleteMailTemplate, getMailTemplate, getMailTemplatePage, sendMail } from "@/api/system/mail/template";
|
||||
import Editor from '@/components/Editor';
|
||||
import { CommonStatusEnum } from "@/utils/constants";
|
||||
import { getSimpleMailAccountList } from "@/api/system/mail/account";
|
||||
|
||||
export default {
|
||||
name: "MailTemplate",
|
||||
components: {
|
||||
Editor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 邮件模版列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
code: null,
|
||||
accountId: null,
|
||||
status: null,
|
||||
createTime: [],
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
name: [{ required: true, message: "模板名称不能为空", trigger: "blur" }],
|
||||
code: [{ required: true, message: "模板编码不能为空", trigger: "blur" }],
|
||||
accountId: [{ required: true, message: "邮箱账号不能为空", trigger: "blur" }],
|
||||
title: [{ required: true, message: "模板标题不能为空", trigger: "blur" }],
|
||||
content: [{ required: true, message: "模板内容不能为空", trigger: "blur" }],
|
||||
status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
|
||||
},
|
||||
// 邮箱账号
|
||||
accountOptions: [],
|
||||
|
||||
// 发送邮箱
|
||||
sendOpen: false,
|
||||
sendForm: {
|
||||
params: [], // 模板的参数列表
|
||||
},
|
||||
sendRules: {
|
||||
mail: [{ required: true, message: "收件邮箱不能为空", trigger: "blur" }],
|
||||
templateCode: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
|
||||
templateParams: { }
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
// 获得邮箱账号列表
|
||||
getSimpleMailAccountList().then(response => {
|
||||
this.accountOptions = response.data
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 执行查询
|
||||
getMailTemplatePage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
accountId: undefined,
|
||||
nickname: undefined,
|
||||
title: undefined,
|
||||
content: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
remark: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加邮件模版";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getMailTemplate(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改邮件模版";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateMailTemplate(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createMailTemplate(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除邮件模版编号为"' + id + '"的数据项?').then(function() {
|
||||
return deleteMailTemplate(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 发送短息按钮 */
|
||||
handleSend(row) {
|
||||
this.resetSend(row);
|
||||
// 设置参数
|
||||
this.sendForm.content = row.content;
|
||||
this.sendForm.params = row.params;
|
||||
this.sendForm.templateCode = row.code;
|
||||
this.sendForm.templateParams = row.params.reduce(function(obj, item) {
|
||||
obj[item] = undefined;
|
||||
return obj;
|
||||
}, {});
|
||||
// 根据 row 重置 rules
|
||||
this.sendRules.templateParams = row.params.reduce(function(obj, item) {
|
||||
obj[item] = { required: true, message: '参数 ' + item + " 不能为空", trigger: "change" };
|
||||
return obj;
|
||||
}, {});
|
||||
// 设置打开
|
||||
this.sendOpen = true;
|
||||
},
|
||||
/** 重置发送邮箱的表单 */
|
||||
resetSend() {
|
||||
// 根据 row 重置表单
|
||||
this.sendForm = {
|
||||
content: undefined,
|
||||
params: undefined,
|
||||
mail: undefined,
|
||||
templateCode: undefined,
|
||||
templateParams: {}
|
||||
};
|
||||
this.resetForm("sendForm");
|
||||
},
|
||||
/** 取消发送邮箱 */
|
||||
cancelSend() {
|
||||
this.sendOpen = false;
|
||||
this.resetSend();
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitSendForm() {
|
||||
this.$refs["sendForm"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
sendMail(this.sendForm).then(response => {
|
||||
this.$modal.msgSuccess("提交发送成功!发送结果,见发送日志编号:" + response.data);
|
||||
this.sendOpen = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -33,7 +33,7 @@
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
|
||||
<el-table-column prop="name" label="菜单名称" :show-overflow-tooltip="true" width="250"></el-table-column>
|
||||
<el-table-column prop="icon" label="图标" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<svg-icon :icon-class="scope.row.icon" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -41,17 +41,17 @@
|
||||
<el-table-column prop="permission" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="80">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:menu:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-plus" @click="handleAdd(scope.row)"
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
<el-form-item label="公告标题" prop="title">
|
||||
<el-input v-model="queryParams.title" placeholder="请输入公告标题" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="操作人员" prop="createBy">
|
||||
<el-input v-model="queryParams.createBy" placeholder="请输入操作人员" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="queryParams.type" placeholder="公告类型" clearable>
|
||||
<el-option v-for="dict in noticeTypeDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
|
||||
<el-form-item label="公告状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="公告状态" clearable>
|
||||
<el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -30,23 +27,23 @@
|
||||
<el-table-column label="序号" align="center" prop="id" width="100" />
|
||||
<el-table-column label="公告标题" align="center" prop="title" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="公告类型" align="center" prop="type" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_NOTICE_TYPE" :value="scope.row.type"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建者" align="center" prop="createBy" width="100" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:notice:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
@@ -136,7 +133,6 @@ export default {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
title: undefined,
|
||||
createBy: undefined,
|
||||
status: undefined
|
||||
},
|
||||
// 表单参数
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user