源文件

This commit is contained in:
gyq
2024-04-24 09:52:04 +08:00
commit 127202beac
386 changed files with 102573 additions and 0 deletions

View File

@@ -0,0 +1,123 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">电报号</label>
<el-input v-model="query.userTelegramId" clearable placeholder="电报号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">用户名称</label>
<el-input v-model="query.userName" clearable placeholder="用户名称" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">业务代码</label>
<el-input v-model="query.bizCode" clearable placeholder="业务代码" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="电报号">
<el-input v-model="form.userTelegramId" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户名称">
<el-input v-model="form.userName" style="width: 370px;" />
</el-form-item>
<el-form-item label="业务代码">
<el-input v-model="form.bizCode" style="width: 370px;" />
</el-form-item>
<el-form-item label="变动金额">
<el-input v-model="form.amount" style="width: 370px;" />
</el-form-item>
<el-form-item label="变动前金额">
<el-input v-model="form.oldBalance" style="width: 370px;" />
</el-form-item>
<el-form-item label="变动后金额">
<el-input v-model="form.newBalance" style="width: 370px;" />
</el-form-item>
<el-form-item label="创建时间">
<el-input v-model="form.createTime" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="userTelegramId" label="电报号" />
<el-table-column prop="userName" label="用户名称" />
<el-table-column prop="bizCode" label="业务代码">
<template slot-scope="scope">
{{ dict.label.biz_code[scope.row.bizCode] }}
</template>
</el-table-column>
<el-table-column prop="amount" label="变动金额" />
<el-table-column prop="oldBalance" label="变动前金额" />
<el-table-column prop="newBalance" label="变动后金额" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column v-if="checkPer(['admin','botUserFlow:edit','botUserFlow:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudBotUserFlow from '@/api/botUserFlow'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, userTelegramId: null, userName: null, bizCode: null, amount: null, oldBalance: null, newBalance: null, createTime: null }
export default {
name: 'BotUserFlow',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['biz_code'],
cruds() {
return CRUD({ title: 'accountFlow', url: 'api/botUserFlow', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBotUserFlow }})
},
data() {
return {
permission: {
add: ['admin', 'botUserFlow:add'],
edit: ['admin', 'botUserFlow:edit'],
del: ['admin', 'botUserFlow:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'userTelegramId', display_name: '电报号' },
{ key: 'userName', display_name: '用户名称' },
{ key: 'bizCode', display_name: '业务代码' }
]
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,196 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label"> 锁定状态</label>
<el-select v-model="query.lockStatus" filterable placeholder="请选择" class="filter-item" @keyup.enter.native="crud.toQuery" >
<el-option
v-for="item in dict.box_account_lock_status"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
<!-- <el-input v-model="query.lockStatus" clearable placeholder=" 锁定状态" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> -->
<label class="el-form-item-label">挖矿状态</label>
<el-select v-model="query.minerStatus" filterable placeholder="请选择" class="filter-item" @keyup.enter.native="crud.toQuery" >
<el-option
v-for="item in dict.box_miner_status"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
<!-- <el-input v-model="query.minerStatus" clearable placeholder="挖矿状态" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> -->
<label class="el-form-item-label">用户钱包地址</label>
<el-input v-model="query.address" clearable placeholder="用户钱包地址" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="下单人">
<el-input v-model="form.userId" style="width: 370px;" />
</el-form-item>
<el-form-item label="购买数量">
<el-input v-model="form.boxNumber" style="width: 370px;" />
</el-form-item>
<el-form-item label=" 预计收益(USDT)">
<el-input v-model="form.payMinerTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label="累计收益">
<el-input v-model="form.payMinerAlready" style="width: 370px;" />
</el-form-item>
<el-form-item label=" 出局进度(百分比)">
<el-input v-model="form.payMinerAlreadyRate" style="width: 370px;" />
</el-form-item>
<el-form-item label=" 锁定状态">
<el-select v-model="form.lockStatus" filterable placeholder="请选择">
<el-option
v-for="item in dict.box_account_lock_status"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="挖矿状态">
<el-select v-model="form.minerStatus" filterable placeholder="请选择">
<el-option
v-for="item in dict.box_miner_status"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label=" 累计静态总收益 每天更新 ">
<el-input v-model="form.payMinerJingTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label=" 累计动态总收益 一次性奖励 下级用户支付成功 立即返现 ">
<el-input v-model="form.payMinerDongTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label=" 累计管理总收益 每天更新 ">
<el-input v-model="form.payMinerGuanTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label=" 累计节点总收益 每天更新 ">
<el-input v-model="form.payMinerJiedTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label="订单描述">
<el-input v-model="form.remark" style="width: 370px;" />
</el-form-item>
<el-form-item label="版本号">
<el-input v-model="form.version" style="width: 370px;" />
</el-form-item>
<el-form-item label="创建时间">
<el-input v-model="form.createTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="更新时间">
<el-input v-model="form.updateTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户钱包地址">
<el-input v-model="form.address" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="address" label="用户钱包地址" />
<el-table-column prop="boxNumber" label="购买数量" />
<el-table-column prop="payMinerTotal" label=" 预计收益(USDT)" />
<el-table-column prop="payMinerAlready" label="累计收益" />
<el-table-column prop="payMinerAlreadyRate" label=" 出局进度(百分比)" />
<el-table-column prop="lockStatus" label=" 锁定状态">
<template slot-scope="scope">
{{ dict.label.box_account_lock_status[scope.row.lockStatus] }}
</template>
</el-table-column>
<el-table-column prop="minerStatus" label="挖矿状态">
<template slot-scope="scope">
{{ dict.label.box_miner_status[scope.row.minerStatus] }}
</template>
</el-table-column>
<el-table-column prop="payMinerJingTotal" label=" 累计静态总收益" />
<el-table-column prop="payMinerDongTotal" label=" 累计动态总收益" />
<el-table-column prop="payMinerGuanTotal" label=" 累计管理总收益" />
<el-table-column prop="payMinerJiedTotal" label=" 累计节点总收益" />
<el-table-column prop="remark" label="订单描述" />
<el-table-column prop="version" label="版本号" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="updateTime" label="更新时间" />
<!-- <el-table-column v-if="checkPer(['admin','viewBoxAccount:edit','viewBoxAccount:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column> -->
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudViewBoxAccount from '@/api/viewBoxAccount'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, userId: null, boxNumber: null, payMinerTotal: null, payMinerAlready: null, payMinerAlreadyRate: null, lockStatus: null, minerStatus: null, payMinerJingTotal: null, payMinerDongTotal: null, payMinerGuanTotal: null, payMinerJiedTotal: null, remark: null, version: null, createTime: null, updateTime: null, address: null }
export default {
name: 'ViewBoxAccount',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['box_account_lock_status', 'box_miner_status'],
cruds() {
return CRUD({ title: '盲盒账户', url: 'api/viewBoxAccount', idField: 'id', sort: 'id,desc', crudMethod: { ...crudViewBoxAccount }})
},
data() {
return {
permission: {
add: ['admin', 'viewBoxAccount:add'],
edit: ['admin', 'viewBoxAccount:edit'],
del: ['admin', 'viewBoxAccount:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'lockStatus', display_name: ' 锁定状态' },
{ key: 'minerStatus', display_name: '挖矿状态' },
{ key: 'address', display_name: '用户钱包地址' }
]
}
},
created() {
this.crud.optShow = {
// edit: true,
reset: true,
// download: true
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

133
src/views/account/index.vue Normal file
View File

@@ -0,0 +1,133 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">钱包地址</label>
<el-input v-model="query.address" clearable placeholder="钱包地址" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id" prop="id">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户主键">
<el-input v-model="form.userId" style="width: 370px;" />
</el-form-item>
<el-form-item label="冻结数量">
<el-input v-model="form.frozenBalance" style="width: 370px;" />
</el-form-item>
<el-form-item label="余额数量">
<el-input v-model="form.balance" style="width: 370px;" />
</el-form-item>
<el-form-item label="">
<el-input v-model="form.lockStatus" style="width: 370px;" />
</el-form-item>
<el-form-item label="版本号">
<el-input v-model="form.version" style="width: 370px;" />
</el-form-item>
<el-form-item label="创建时间">
<el-input v-model="form.createTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="更新时间">
<el-input v-model="form.updateTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="钱包地址">
<el-input v-model="form.address" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="id" v-if="false"/>
<el-table-column prop="address" label="钱包地址" />
<el-table-column prop="userId" label="用户主键" v-if="false" />
<el-table-column prop="frozenBalance" label="冻结数量" />
<el-table-column prop="balance" label="余额数量" />
<el-table-column prop="lockStatus" label="">
<template slot-scope="scope">
{{ dict.label.account_lock[scope.row.lockStatus] }}
</template>
</el-table-column>
<el-table-column prop="version" label="版本号" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="updateTime" label="更新时间" />
<!-- <el-table-column v-if="checkPer(['admin','viewAccountInfo:edit','viewAccountInfo:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column> -->
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudViewAccountInfo from '@/api/viewAccountInfo'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, userId: null, frozenBalance: null, balance: null, lockStatus: null, version: null, createTime: null, updateTime: null, address: null }
export default {
name: 'ViewAccountInfo',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['account_lock'],
cruds() {
return CRUD({ title: 'acountinfo', url: 'api/viewAccountInfo', idField: 'id', sort: 'id,desc', crudMethod: { ...crudViewAccountInfo }})
},
data() {
return {
permission: {
add: ['admin', 'viewAccountInfo:add'],
edit: ['admin', 'viewAccountInfo:edit'],
del: ['admin', 'viewAccountInfo:del']
},
rules: {
id: [
{ required: true, message: '不能为空', trigger: 'blur' }
]
},
queryTypeOptions: [
{ key: 'address', display_name: '钱包地址' }
]
}
},
created() {
this.crud.optShow = {
// edit: true,
reset: true,
// download: true
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,164 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">交易哈希</label>
<el-input v-model="query.txHash" clearable placeholder="交易哈希" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">转出地址</label>
<el-input v-model="query.fromAddress" clearable placeholder="转出地址" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">到账地址</label>
<el-input v-model="query.toAddress" clearable placeholder="到账地址" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">链上状态</label>
<el-select v-model="query.status" filterable placeholder="请选择" class="filter-item" @keyup.enter.native="crud.toQuery">
<el-option
v-for="item in dict.flow_chain_status"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
<!-- <el-input v-model="query.status" clearable placeholder="链上状态" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> -->
<label class="el-form-item-label">钱包地址</label>
<el-input v-model="query.address" clearable placeholder="钱包地址" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" title="状态编辑" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id" v-if="false">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="PETK数量">
<el-input v-model="form.amountPetk" style="width: 370px;" />
</el-form-item>
<el-form-item label="链上状态">
<template>
<el-select v-model="form.status" placeholder="请选择">
<el-option
v-for="item in chianstatus"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</template>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="address" label="钱包地址" />
<el-table-column prop="id" label="id" v-if="false"/>
<el-table-column prop="userId" label="操作用户主键" v-if="false" />
<el-table-column prop="userAccountId" label="操作用户账户主键" v-if="false"/>
<el-table-column prop="outFee" label="提币手续费" />
<el-table-column prop="amountUsdt" label="USDT数量" />
<el-table-column prop="amountPetk" label="PETK数量" />
<el-table-column prop="pricePetk" label="当前价格" />
<el-table-column prop="txHash" label="交易哈希" />
<el-table-column prop="fromAddress" label="转出地址" />
<el-table-column prop="toAddress" label="到账地址" />
<el-table-column prop="status" label="链上状态">
<template slot-scope="scope">
{{ dict.label.flow_chain_status[scope.row.status] }}
</template>
</el-table-column>
<el-table-column prop="sendStatus" label="发送状态">
<template slot-scope="scope">
{{ dict.label.flow_send_status[scope.row.sendStatus] }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="updateTime" label="更新时间" />
<el-table-column v-if="checkPer(['admin','viewWalletFlow:edit',,'viewWalletFlow:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:disabled-dle="scope.row.id ===scope.row.id"
:disabled-edit="(scope.row.status==='success')||(scope.row.status==='notpass')"
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudViewWalletFlow from '@/api/viewWalletFlow'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, userId: null, userAccountId: null, outFee: null, amountUsdt: null, amountPetk: null, pricePetk: null, txHash: null, fromAddress: null, toAddress: null, status: null, sendStatus: null, createTime: null, updateTime: null, address: null }
export default {
name: 'ViewWalletFlow',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['flow_chain_status', 'flow_send_status'],
cruds() {
return CRUD({ title: 'walletflow', url: 'api/viewWalletFlow', idField: 'id', sort: 'id,desc', crudMethod: { ...crudViewWalletFlow }})
},
data() {
return {
permission: {
add: ['admin', 'viewWalletFlow:add'],
edit: ['admin', 'viewWalletFlow:edit']
},
value: '',
chianstatus:[{label:"请选择",value:"waitPass"},{label:"通过",value:"waitsend"},{label:"拒绝",value:"notpass"}],
rules: {
},
queryTypeOptions: [
{ key: 'txHash', display_name: '交易哈希' },
{ key: 'fromAddress', display_name: '转出地址' },
{ key: 'toAddress', display_name: '到账地址' },
{ key: 'status', display_name: '链上状态' },
{ key: 'address', display_name: '钱包地址' }
]
}
},
created() {
this.crud.optShow = {
edit: true,
reset: true,
// download: true
}
},
created() {
this.crud.optShow = {
// edit: true,
reset: true,
// download: true
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,92 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">按钮名称</label>
<el-input v-model="query.buttonName" clearable placeholder="按钮名称" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="按钮名称">
<el-input v-model="form.buttonName" style="width: 370px;" />
</el-form-item>
<el-form-item label="按钮值">
<el-input v-model="form.buttonValue" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="buttonName" label="按钮名称" />
<el-table-column prop="buttonValue" label="按钮值" />
<el-table-column v-if="checkPer(['admin','botButtonConfig:edit','botButtonConfig:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudBotButtonConfig from '@/api/botButtonConfig'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, buttonName: null, buttonValue: null }
export default {
name: 'BotButtonConfig',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({ title: 'buttonConfig', url: 'api/botButtonConfig', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBotButtonConfig }})
},
data() {
return {
permission: {
add: ['admin', 'botButtonConfig:add'],
edit: ['admin', 'botButtonConfig:edit'],
del: ['admin', 'botButtonConfig:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'buttonName', display_name: '按钮名称' }
]
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

101
src/views/bot/index.vue Normal file
View File

@@ -0,0 +1,101 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">元素键值</label>
<el-input v-model="query.configKey" clearable placeholder="元素键值" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">元素值</label>
<el-input v-model="query.configValue" clearable placeholder="元素值" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id" v-show="false">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="元素键值">
<el-input v-model="form.configKey" style="width: 370px;" />
</el-form-item>
<el-form-item label="元素值">
<el-input v-model="form.configValue" style="width: 370px;" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="form.remark" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="id" v-if="false"/>
<el-table-column prop="configKey" label="元素键值" />
<el-table-column prop="configValue" label="元素值" />
<el-table-column prop="remark" label="描述" />
<el-table-column v-if="checkPer(['admin','botConfig:edit','botConfig:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
:disabled-dle="scope.row.id === scope.row.id"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudBotConfig from '@/api/botConfig'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, configKey: null, configValue: null, remark: null }
export default {
name: 'BotConfig',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({ title: 'botConfig', url: 'api/botConfig', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBotConfig }})
},
data() {
return {
permission: {
add: ['admin', 'botConfig:add'],
edit: ['admin', 'botConfig:edit'],
del: ['admin', 'botConfig:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'configKey', display_name: '元素键值' },
{ key: 'configValue', display_name: '元素值' }
]
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

181
src/views/botUser/index.vue Normal file
View File

@@ -0,0 +1,181 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">父级电报号</label>
<el-input v-model="query.fatherTelegramId" clearable placeholder="父级电报号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">电报号</label>
<el-input v-model="query.userTelegramId" clearable placeholder="电报号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">用户名称</label>
<el-input v-model="query.userName" clearable placeholder="用户名称" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">用户代码</label>
<el-input v-model="query.userCode" clearable placeholder="用户代码" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">用户状态</label>
<el-input v-model="query.botStatus" clearable placeholder="用户状态" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id" v-show="false">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="fatherId">
<el-input v-model="form.fatherId" style="width: 370px;" />
</el-form-item>
<el-form-item label="父级电报号">
<el-input v-model="form.fatherTelegramId" style="width: 370px;" />
</el-form-item>
<el-form-item label="电报号">
<el-input v-model="form.userTelegramId" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户名称">
<el-input v-model="form.userName" style="width: 370px;" />
</el-form-item>
<el-form-item label="组电报号">
<el-input v-model="form.groupTelegramId" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户代码">
<el-input v-model="form.userCode" style="width: 370px;" />
</el-form-item>
<el-form-item label="userPayPass" v-show="false">
<el-input v-model="form.userPayPass" style="width: 370px;" />
</el-form-item>
<el-form-item label="bombStatus">
<el-input v-model="form.bombStatus" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户状态">
<el-select v-model="form.botStatus" filterable placeholder="请选择">
<el-option
v-for="item in dict.bot_status"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="总充值">
<el-input v-model="form.usdtRechargeTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label="总提现">
<el-input v-model="form.usdtWithdrawTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label="总资金">
<el-input v-model="form.balance" style="width: 370px;" />
</el-form-item>
<el-form-item label="冻结资金">
<el-input v-model="form.freezeBalance" style="width: 370px;" />
</el-form-item>
<el-form-item label="版本号">
<el-input v-model="form.version" style="width: 370px;" />
</el-form-item>
<el-form-item label="创建时间">
<el-input v-model="form.createTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="更新时间">
<el-input v-model="form.updateTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="语言">
<el-input v-model="form.userLanguage" style="width: 370px;" />
</el-form-item>
<!-- <el-form-item label="质押资金">
<el-input v-model="form.chipBalance" style="width: 370px;" />
</el-form-item>
<el-form-item label="绑定时间">
<el-input v-model="form.fatherBindTime" style="width: 370px;" />
</el-form-item> -->
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="fatherTelegramId" label="父级电报号" />
<el-table-column prop="userTelegramId" label="电报号" />
<el-table-column prop="userName" label="用户名称" />
<el-table-column prop="groupTelegramId" label="组电报号" />
<el-table-column prop="userCode" label="用户代码" />
<el-table-column prop="bombStatus" label="bombStatus" />
<el-table-column prop="botStatus" label="用户状态">
<template slot-scope="scope">
{{ dict.label.bot_status[scope.row.botStatus] }}
</template>
</el-table-column>
<el-table-column prop="usdtRechargeTotal" label="总充值" />
<el-table-column prop="usdtWithdrawTotal" label="总提现" />
<el-table-column prop="balance" label="总资金" />
<el-table-column prop="freezeBalance" label="冻结资金" />
<el-table-column prop="version" label="版本号" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="updateTime" label="更新时间" />
<el-table-column prop="userLanguage" label="语言" />
<!-- <el-table-column prop="chipBalance" label="质押资金" />
<el-table-column prop="fatherBindTime" label="绑定时间" /> -->
<el-table-column v-if="checkPer(['admin','botUser:edit','botUser:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudBotUser from '@/api/botUser'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, fatherId: null, fatherTelegramId: null, userTelegramId: null, userName: null, groupTelegramId: null, userCode: null, userPayPass: null, bombStatus: null, botStatus: null, usdtRechargeTotal: null, usdtWithdrawTotal: null, balance: null, freezeBalance: null, version: null, createTime: null, updateTime: null, userLanguage: null, chipBalance: null, fatherBindTime: null }
export default {
name: 'BotUser',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['bot_status'],
cruds() {
return CRUD({ title: 'BotUserController', url: 'api/botUser', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBotUser }})
},
data() {
return {
permission: {
add: ['admin', 'botUser:add'],
edit: ['admin', 'botUser:edit'],
del: ['admin', 'botUser:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'fatherTelegramId', display_name: '父级电报号' },
{ key: 'userTelegramId', display_name: '电报号' },
{ key: 'userName', display_name: '用户名称' },
{ key: 'userCode', display_name: '用户代码' },
{ key: 'botStatus', display_name: '用户状态' }
]
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,110 @@
<template>
<div class="dashboard-container">
<div class="dashboard-editor-container">
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<heat-map />
</el-row>
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<radar-chart />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<sunburst />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<gauge />
</div>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12">
<div class="chart-wrapper">
<rich />
</div>
</el-col>
<el-col :span="12">
<div class="chart-wrapper">
<theme-river />
</div>
</el-col>
</el-row>
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="12">
<div class="chart-wrapper">
<graph />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="12">
<div class="chart-wrapper">
<sankey />
</div>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12">
<div class="chart-wrapper">
<scatter />
</div>
</el-col>
<el-col :span="12">
<div class="chart-wrapper">
<point />
</div>
</el-col>
</el-row>
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<div class="chart-wrapper">
<category />
</div>
</el-row>
</div>
</div>
</template>
<script>
import RadarChart from '@/components/Echarts/RadarChart'
import HeatMap from '@/components/Echarts/HeatMap'
import Gauge from '@/components/Echarts/Gauge'
import Rich from '@/components/Echarts/Rich'
import ThemeRiver from '@/components/Echarts/ThemeRiver'
import Sunburst from '@/components/Echarts/Sunburst'
import Graph from '@/components/Echarts/Graph'
import Sankey from '@/components/Echarts/Sankey'
import Scatter from '@/components/Echarts/Scatter'
import Category from '@/components/Echarts/Category'
import Point from '@/components/Echarts/Point'
export default {
name: 'Echarts',
components: {
Point,
Category,
Graph,
HeatMap,
RadarChart,
Sunburst,
Gauge,
Rich,
ThemeRiver,
Sankey,
Scatter
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {
padding: 18px 22px 22px 22px;
background-color: rgb(240, 242, 245);
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<div class="app-container">
<p class="warn-content">
富文本基于
<el-link type="primary" href="https://www.kancloud.cn/wangfupeng/wangeditor3/332599" target="_blank">wangEditor</el-link>
</p>
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="15" :lg="15" :xl="15">
<div ref="editor" class="text" />
</el-col>
<el-col :xs="24" :sm="24" :md="9" :lg="9" :xl="9">
<div v-html="editorContent" />
</el-col>
</el-row>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { upload } from '@/utils/upload'
import E from 'wangeditor'
export default {
name: 'Editor',
data() {
return {
editorContent:
`
<ul>
<li>更多帮助请查看官方文档:<a style="color: #42b983" target="_blank" href="https://www.wangeditor.com/doc/">wangEditor</a></li>
</ul>
`
}
},
computed: {
...mapGetters([
'imagesUploadApi',
'baseApi'
])
},
mounted() {
const _this = this
var editor = new E(this.$refs.editor)
// 自定义菜单配置
editor.config.zIndex = 5
// 文件上传
editor.config.customUploadImg = function(files, insert) {
// files 是 input 中选中的文件列表
// insert 是获取图片 url 后,插入到编辑器的方法
files.forEach(image => {
upload(_this.imagesUploadApi, image).then(res => {
const data = res.data
const url = _this.baseApi + '/file/' + data.type + '/' + data.realName
insert(url)
})
})
}
editor.config.onchange = (html) => {
this.editorContent = html
}
editor.create()
// 初始化数据
editor.txt.html(this.editorContent)
}
}
</script>
<style scoped>
.text {
text-align:left;
}
::v-deep .w-e-text-container {
height: 420px !important;
}
</style>

View File

@@ -0,0 +1,55 @@
<template>
<div class="app-container">
<p class="warn-content">
Markdown 基于
<el-link type="primary" href="https://github.com/hinesboy/mavonEditor" target="_blank">MavonEditor</el-link>
</p>
<mavon-editor ref="md" :style="'height:' + height" @imgAdd="imgAdd" />
</div>
</template>
<script>
import { upload } from '@/utils/upload'
import { mapGetters } from 'vuex'
import { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
export default {
name: 'Markdown',
components: {
mavonEditor
},
data() {
return {
height: document.documentElement.clientHeight - 200 + 'px'
}
},
computed: {
...mapGetters([
'imagesUploadApi',
'baseApi'
])
},
mounted() {
const that = this
window.onresize = function temp() {
that.height = document.documentElement.clientHeight - 200 + 'px'
}
},
methods: {
imgAdd(pos, $file) {
upload(this.imagesUploadApi, $file).then(res => {
const data = res.data
const url = this.baseApi + '/file/' + data.type + '/' + data.realName
this.$refs.md.$img2Url(pos, url)
})
}
}
}
</script>
<style scoped>
.v-note-wrapper.shadow {
z-index: 5;
}
</style>

View File

@@ -0,0 +1,207 @@
<template>
<div class="app-container">
<p class="warn-content">
Yaml编辑器 基于
<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>
主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>
</p>
<Yaml :value="value" :height="height" />
</div>
</template>
<script>
import Yaml from '@/components/YamlEdit/index'
export default {
name: 'YamlEdit',
components: { Yaml },
data() {
return {
height: document.documentElement.clientHeight - 210 + 'px',
value: '# 展示数据如需更换主题请在src/components/YamlEdit 目录中搜索原主题名称进行替换\n' +
'\n' +
'# ===================================================================\n' +
'# Spring Boot configuration.\n' +
'#\n' +
'# This configuration will be overridden by the Spring profile you use,\n' +
'# for example application-dev.yml if you use the "dev" profile.\n' +
'#\n' +
'# More information on profiles: https://www.jhipster.tech/profiles/\n' +
'# More information on configuration properties: https://www.jhipster.tech/common-application-properties/\n' +
'# ===================================================================\n' +
'\n' +
'# ===================================================================\n' +
'# Standard Spring Boot properties.\n' +
'# Full reference is available at:\n' +
'# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html\n' +
'# ===================================================================\n' +
'\n' +
'eureka:\n' +
' client:\n' +
' enabled: true\n' +
' healthcheck:\n' +
' enabled: true\n' +
' fetch-registry: true\n' +
' register-with-eureka: true\n' +
' instance-info-replication-interval-seconds: 10\n' +
' registry-fetch-interval-seconds: 10\n' +
' instance:\n' +
' appname: product\n' +
' instanceId: product:${spring.application.instance-id:${random.value}}\n' +
' #instanceId: 127.0.0.1:9080\n' +
' lease-renewal-interval-in-seconds: 5\n' +
' lease-expiration-duration-in-seconds: 10\n' +
' status-page-url-path: ${management.endpoints.web.base-path}/info\n' +
' health-check-url-path: ${management.endpoints.web.base-path}/health\n' +
' metadata-map:\n' +
' zone: primary # This is needed for the load balancer\n' +
' profile: ${spring.profiles.active}\n' +
' version: ${info.project.version:}\n' +
' git-version: ${git.commit.id.describe:}\n' +
' git-commit: ${git.commit.id.abbrev:}\n' +
' git-branch: ${git.branch:}\n' +
'ribbon:\n' +
' ReadTimeout: 120000\n' +
' ConnectTimeout: 300000\n' +
' eureka:\n' +
' enabled: true\n' +
'zuul:\n' +
' host:\n' +
' connect-timeout-millis: 5000\n' +
' max-per-route-connections: 10000\n' +
' max-total-connections: 5000\n' +
' socket-timeout-millis: 60000\n' +
' semaphore:\n' +
' max-semaphores: 500\n' +
'\n' +
'feign:\n' +
' hystrix:\n' +
' enabled: true\n' +
' client:\n' +
' config:\n' +
' default:\n' +
' connectTimeout: 500000\n' +
' readTimeout: 500000\n' +
'\n' +
'# See https://github.com/Netflix/Hystrix/wiki/Configuration\n' +
'hystrix:\n' +
' command:\n' +
' default:\n' +
' circuitBreaker:\n' +
' sleepWindowInMilliseconds: 100000\n' +
' forceClosed: true\n' +
' execution:\n' +
' isolation:\n' +
'# strategy: SEMAPHORE\n' +
'# See https://github.com/spring-cloud/spring-cloud-netflix/issues/1330\n' +
' thread:\n' +
' timeoutInMilliseconds: 60000\n' +
' shareSecurityContext: true\n' +
'\n' +
'management:\n' +
' endpoints:\n' +
' web:\n' +
' base-path: /management\n' +
' exposure:\n' +
' include: ["configprops", "env", "health", "info", "threaddump"]\n' +
' endpoint:\n' +
' health:\n' +
' show-details: when_authorized\n' +
' info:\n' +
' git:\n' +
' mode: full\n' +
' health:\n' +
' mail:\n' +
' enabled: false # When using the MailService, configure an SMTP server and set this to true\n' +
' metrics:\n' +
' enabled: false # http://micrometer.io/ is disabled by default, as we use http://metrics.dropwizard.io/ instead\n' +
'\n' +
'spring:\n' +
' application:\n' +
' name: product\n' +
' jpa:\n' +
' open-in-view: false\n' +
' hibernate:\n' +
' ddl-auto: update\n' +
' naming:\n' +
' physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy\n' +
' implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy\n' +
' messages:\n' +
' basename: i18n/messages\n' +
' mvc:\n' +
' favicon:\n' +
' enabled: false\n' +
' thymeleaf:\n' +
' mode: HTML\n' +
'security:\n' +
' oauth2:\n' +
' resource:\n' +
' filter-order: 3\n' +
'\n' +
'server:\n' +
' servlet:\n' +
' session:\n' +
' cookie:\n' +
' http-only: true\n' +
'\n' +
'# Properties to be exposed on the /info management endpoint\n' +
'info:\n' +
' # Comma separated list of profiles that will trigger the ribbon to show\n' +
' display-ribbon-on-profiles: "dev"\n' +
'\n' +
'# ===================================================================\n' +
'# JHipster specific properties\n' +
'#\n' +
'# Full reference is available at: https://www.jhipster.tech/common-application-properties/\n' +
'# ===================================================================\n' +
'\n' +
'jhipster:\n' +
' async:\n' +
' core-pool-size: 2\n' +
' max-pool-size: 50\n' +
' queue-capacity: 10000\n' +
' # By default CORS is disabled. Uncomment to enable.\n' +
' #cors:\n' +
' #allowed-origins: "*"\n' +
' #allowed-methods: "*"\n' +
' #allowed-headers: "*"\n' +
' #exposed-headers: "Authorization,Link,X-Total-Count"\n' +
' #allow-credentials: true\n' +
' #max-age: 1800\n' +
' mail:\n' +
' from: product@localhost\n' +
' swagger:\n' +
' default-include-pattern: /api/.*\n' +
' title: product API\n' +
' description: product API documentation\n' +
' version: 0.0.1\n' +
' terms-of-service-url:\n' +
' contact-name:\n' +
' contact-url:\n' +
' contact-email:\n' +
' license:\n' +
' license-url:\n' +
'\n' +
'# ===================================================================\n' +
'# Application specific properties\n' +
'# Add your own application properties here, see the ApplicationProperties class\n' +
'# to have type-safe configuration, like in the JHipsterProperties above\n' +
'#\n' +
'# More documentation is available at:\n' +
'# https://www.jhipster.tech/common-application-properties/\n' +
'# ===================================================================\n' +
'\n' +
'# application:\n'
}
},
mounted() {
const that = this
window.onresize = function temp() {
that.height = document.documentElement.clientHeight - 210 + 'px'
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,41 @@
<template>
<div class="app-container">
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
<el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
<el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
</el-table>
</div>
</template>
<script>
import UploadExcelComponent from '@/components/UploadExcel/index.vue'
export default {
name: 'UploadExcel',
components: { UploadExcelComponent },
data() {
return {
tableData: [],
tableHeader: []
}
},
methods: {
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: '请不要上传大于1m的文件.',
type: 'warning'
})
return false
},
handleSuccess({ results, header }) {
this.tableData = results
this.tableHeader = header
}
}
}
</script>

View File

@@ -0,0 +1,74 @@
const elementIcons = [
'info',
'error',
'success',
'warning',
'question',
'back',
'arrow-left',
'arrow-down',
'arrow-right',
'arrow-up',
'caret-left',
'caret-bottom',
'caret-top',
'caret-right',
'd-arrow-left',
'd-arrow-right',
'minus',
'plus',
'remove',
'circle-plus',
'remove-outline',
'circle-plus-outline',
'close',
'check',
'circle-close',
'circle-check',
'circle-close-outline',
'circle-check-outline',
'zoom-out',
'zoom-in',
'd-caret',
'sort',
'sort-down',
'sort-up',
'tickets',
'document',
'goods',
'sold-out',
'news',
'message',
'date',
'printer',
'time',
'bell',
'mobile-phone',
'service',
'view',
'menu',
'more',
'more-outline',
'star-on',
'star-off',
'location',
'location-outline',
'phone',
'phone-outline',
'picture',
'picture-outline',
'delete',
'search',
'edit',
'edit-outline',
'rank',
'refresh',
'share',
'setting',
'upload',
'upload2',
'download',
'loading'
]
export default elementIcons

View File

@@ -0,0 +1,97 @@
<template>
<div class="icons-container">
<aside>
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
</a>
</aside>
<el-tabs type="border-card">
<el-tab-pane label="Icons">
<div class="grid">
<div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
<el-tooltip placement="top">
<div slot="content">
{{ generateIconCode(item) }}
</div>
<div class="icon-item">
<svg-icon :icon-class="item" class-name="disabled" />
<span>{{ item }}</span>
</div>
</el-tooltip>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="Element-UI Icons">
<div class="grid">
<div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
<el-tooltip placement="top">
<div slot="content">
{{ generateElementIconCode(item) }}
</div>
<div class="icon-item">
<i :class="'el-icon-' + item" />
<span>{{ item }}</span>
</div>
</el-tooltip>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import clipboard from '@/utils/clipboard'
import svgIcons from './svg-icons'
import elementIcons from './element-icons'
export default {
name: 'Icons',
data() {
return {
svgIcons,
elementIcons
}
},
methods: {
generateIconCode(symbol) {
return `<svg-icon icon-class="${symbol}" />`
},
generateElementIconCode(symbol) {
return `<i class="el-icon-${symbol}" />`
},
handleClipboard(text, event) {
clipboard(text, event)
}
}
}
</script>
<style lang="scss" scoped>
.icons-container {
margin: 10px 20px 0;
overflow: hidden;
.grid {
position: relative;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.icon-item {
margin: 20px;
height: 85px;
text-align: center;
width: 100px;
float: left;
font-size: 30px;
color: #24292e;
cursor: pointer;
}
span {
display: block;
font-size: 16px;
margin-top: 10px;
}
.disabled {
pointer-events: none;
}
}
</style>

View File

@@ -0,0 +1,10 @@
const req = require.context('../../../assets/icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()
const re = /\.\/(.*)\.svg/
const svgIcons = requireAll(req).map(i => {
return i.match(re)[1]
})
export default svgIcons

View File

@@ -0,0 +1,277 @@
<template>
<div>
<el-dialog title="添加优惠券" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-form-item label="优惠卷名称" prop="title">
<el-input v-model="form.title" placeholder="请输入优惠卷名称" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-radio-group v-model="form.classType">
<el-radio :label="item.value" v-for="item in couponEnum.classType" :key="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选择商品" v-if="form.classType == 'product'" prop="classType">
<div>
<el-button type="primary" icon="el-icon-plus" @click="$refs.shopListRef.show([...productIds])">
添加商品
</el-button>
</div>
<div class="shop_list">
<div class="item_wrap" v-for="(item, index) in productIds" :key="item.id"
@click="productIds.splice(index, 1)">
<div class="item" :data-index="index + 1">
<el-image :src="item.coverImg" style="width: 100%;height: 100%;"></el-image>
</div>
<div class="name">{{ item.name }}</div>
</div>
</div>
</el-form-item>
<el-form-item label="优惠类型">
<el-radio-group v-model="form.type">
<el-radio :label="item.value" v-for="item in couponEnum.type" :key="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="满减限制" v-if="form.type == '0'">
<el-input-number v-model="form.limitAmount" controls-position="right" :min="0"></el-input-number>
</el-form-item>
<el-form-item label="折扣" v-if="form.type == '1'">
<el-input-number v-model="form.ratio" controls-position="right" :min="1"></el-input-number>
</el-form-item>
<el-form-item label="优惠券面额">
<el-input-number v-model="form.amount" controls-position="right" :min="0"></el-input-number>
</el-form-item>
<el-form-item label="发放数量">
<el-input-number v-model="form.number" controls-position="right" :min="1"></el-input-number>
</el-form-item>
<el-form-item label="限领数量">
<el-input-number v-model="form.limitNumber" controls-position="right" :min="1"></el-input-number>
</el-form-item>
<el-form-item label="有效期">
<el-radio-group v-model="form.effectType">
<el-radio :label="item.value" v-for="item in couponEnum.effectType" :key="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="有效时间" v-if="form.effectType == 1">
<el-date-picker v-model="selectTime" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']"
value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="form.status" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="loading" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
<shopList ref="shopListRef" @success="slectShop" />
</div>
</template>
<script>
import couponEnum from './../couponEnum'
import shopList from '@/components/shopList'
import { tbMerchantCoupon } from '@/api/shop'
export default {
components: { shopList },
data() {
const validateProduct = (rule, value, callback) => {
if (!this.productIds.length) {
callback(new Error('请选择商品'))
} else {
callback()
}
}
return {
couponEnum,
dialogVisible: false,
loading: false,
selectTime: [],
form: {
id: '',
title: '',
classType: 'product',
type: '0',
limitAmount: '',
ratio: '1',
amount: '',
number: '1',
limitNumber: '1',
effectType: '0',
fromTime: '',
toTime: '',
relationIds: '',
status: 1
},
rules: {
title: [
{
required: true,
message: ' ',
trigger: 'blur'
}
],
classType: [
{
required: true,
validator: validateProduct,
trigger: 'change'
}
]
},
resetForm: '',
productIds: []
}
},
mounted() {
this.resetForm = { ...this.form }
},
methods: {
// 选择商品
slectShop(res) {
if (this.productIds.length) {
res.map(async item => {
if (!await this.checkShop(item.id)) {
this.productIds.push({ ...item })
}
})
} else {
this.productIds = res
}
},
// 判断是否存在重复商品
checkShop(id) {
let falg = false
this.productIds.map(item => {
if (item.id == id) {
falg = true
}
})
return falg
},
// 提交
onSubmitHandle() {
console.log(this.form)
this.$refs.form.validate(async valid => {
if (valid) {
try {
this.loading = true
this.form.shopId = localStorage.getItem('shopId')
this.form.fromTime = this.selectTime[0] || ''
this.form.toTime = this.selectTime[1] || ''
let arr = []
if (this.form.classType == 'product') {
arr = this.productIds.map(item => item.id)
}
this.form.relationIds = arr.join(',')
let res = await tbMerchantCoupon(this.form, this.form.id ? 'put' : 'post')
this.$emit('success', res)
this.close()
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
this.loading = false
} catch (error) {
this.loading = false
console.log(error)
}
}
})
},
show(obj) {
this.dialogVisible = true
if (obj && obj.id) {
this.form = { ...obj }
}
},
close() {
this.dialogVisible = false
},
reset() {
this.form = { ...this.resetForm }
}
}
}
</script>
<style scoped lang="scss">
.shop_list {
display: flex;
flex-wrap: wrap;
.item_wrap {
$size: 80px;
.item {
$radius: 4px;
width: $size;
height: $size;
border-radius: $radius;
overflow: hidden;
position: relative;
margin-right: 10px;
margin-top: 10px;
&:hover {
cursor: pointer;
}
&::after {
content: attr(data-index);
font-size: 12px;
height: 20px;
display: flex;
padding: 0 10px;
border-radius: 0 0 $radius 0;
align-items: center;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
color: #fff;
position: absolute;
top: 0;
left: 0;
z-index: 10;
}
&::before {
content: '删除';
font-size: 12px;
width: 100%;
height: 20px;
display: flex;
padding: 0 10px;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
color: #fff;
position: absolute;
bottom: 0;
left: 0;
z-index: 10;
transition: all .1s ease-in-out;
}
}
.name {
width: $size;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
</style>

View File

@@ -0,0 +1,32 @@
export default {
classType: [
{
value: 'product',
label: '商品券'
},
{
value: 'common',
label: '通用券'
}
],
type: [
{
value: '0',
label: '满减'
},
{
value: '1',
label: '折扣'
}
],
effectType: [
{
value: '0',
label: '一直有效'
},
{
value: '1',
label: '时限有效'
}
]
}

View File

@@ -0,0 +1,118 @@
<template>
<div class="app-container">
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addCoupon.show()">
添加优惠券
</el-button>
</div>
<div class="head-container">
<el-table :data="tableData.data" v-loading="tableData.loading">
<el-table-column label="优惠券名称" prop="title"></el-table-column>
<el-table-column label="类型" prop="classType">
<template v-slot="scope">
{{ scope.row.classType | classTypeFilter }}
</template>
</el-table-column>
<el-table-column label="优惠类型" prop="type">
<template v-slot="scope">
{{ scope.row.type | typeFilter }}
</template>
</el-table-column>
<el-table-column label="折扣" prop="ratio"></el-table-column>
<el-table-column label="面额" prop="amount"></el-table-column>
<el-table-column label="满减限制" prop="limitAmount"></el-table-column>
<el-table-column label="发放数量" prop="number"></el-table-column>
<el-table-column label="限领数量" prop="limitNumber"></el-table-column>
<el-table-column label="剩余数量" prop="leftNumber"></el-table-column>
<el-table-column label="有效期" prop="effectType">
<template v-slot="scope">
{{ scope.row.effectType | effectTypeFilter }}
</template>
</el-table-column>
<el-table-column label="开始时间" prop="fromTime"></el-table-column>
<el-table-column label="到期时间" prop="toTime"></el-table-column>
<el-table-column label="商品列表" prop="relationIds" width="200">
<template v-slot="scope">
<div style="display: flex;" v-if="scope.row.classType == 'product'">
<el-image :src="scope.row.coverImg" style="width: 30px;height: 30px;"></el-image>
<span style="margin-left: 10px;">{{ scope.row.name }}</span>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" @size-change="sizeChange"
layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
<addCoupon ref="addCoupon" @success="resetHandle" />
</div>
</template>
<script>
import couponEnum from './couponEnum'
import addCoupon from './components/addCoupon.vue'
import { tbMerchantCouponGet } from '@/api/shop'
export default {
components: { addCoupon },
data() {
return {
tableData: {
data: [],
page: 0,
size: 10,
loading: false,
total: 0
}
}
},
filters: {
classTypeFilter(value) {
return couponEnum.classType.find(item => item.value == value).label
},
typeFilter(value) {
return couponEnum.type.find(item => item.value == value).label
},
effectTypeFilter(value) {
return couponEnum.effectType.find(item => item.value == value).label
}
},
mounted() {
this.getTableData()
},
methods: {
// 重置查询
resetHandle() {
this.page = 0
this.getTableData()
},
// 分页大小改变
sizeChange(e) {
this.tableData.size = e
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbMerchantCouponGet({
page: this.tableData.page,
size: this.tableData.size,
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.data = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error)
}
}
}
}
</script>

View File

@@ -0,0 +1,135 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '350px'
},
autoResize: {
type: Boolean,
default: true
},
chartData: {
type: Object,
required: true
}
},
data() {
return {
chart: null
}
},
watch: {
chartData: {
deep: true,
handler(val) {
this.setOptions(val)
}
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
},
setOptions({ expectedData, actualData } = {}) {
this.chart.setOption({
xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
boundaryGap: false,
axisTick: {
show: false
}
},
grid: {
left: 10,
right: 10,
bottom: 20,
top: 30,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: {
axisTick: {
show: false
}
},
legend: {
data: ['expected', 'actual']
},
series: [{
name: 'expected', itemStyle: {
normal: {
color: '#FF005A',
lineStyle: {
color: '#FF005A',
width: 2
}
}
},
smooth: true,
type: 'line',
data: expectedData,
animationDuration: 2800,
animationEasing: 'cubicInOut'
},
{
name: 'actual',
smooth: true,
type: 'line',
itemStyle: {
normal: {
color: '#3888fa',
lineStyle: {
color: '#3888fa',
width: 2
},
areaStyle: {
color: '#f3f8ff'
}
}
},
data: actualData,
animationDuration: 2800,
animationEasing: 'quadraticOut'
}]
})
}
}
}
</script>

View File

@@ -0,0 +1,181 @@
<template>
<el-row :gutter="40" class="panel-group">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
<div class="card-panel-icon-wrapper icon-people">
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
New Visits
</div>
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('messages')">
<div class="card-panel-icon-wrapper icon-message">
<svg-icon icon-class="message" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
Messages
</div>
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('purchases')">
<div class="card-panel-icon-wrapper icon-money">
<svg-icon icon-class="money" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
Purchases
</div>
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
Shoppings
</div>
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
</div>
</div>
</el-col>
</el-row>
</template>
<script>
import CountTo from 'vue-count-to'
export default {
components: {
CountTo
},
methods: {
handleSetLineChartData(type) {
this.$emit('handleSetLineChartData', type)
}
}
}
</script>
<style lang="scss" scoped>
.panel-group {
margin-top: 18px;
.card-panel-col {
margin-bottom: 32px;
}
.card-panel {
height: 108px;
cursor: pointer;
font-size: 12px;
position: relative;
overflow: hidden;
color: #666;
background: #fff;
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
border-color: rgba(0, 0, 0, .05);
&:hover {
.card-panel-icon-wrapper {
color: #fff;
}
.icon-people {
background: #40c9c6;
}
.icon-message {
background: #36a3f7;
}
.icon-money {
background: #f4516c;
}
.icon-shopping {
background: #34bfa3
}
}
.icon-people {
color: #40c9c6;
}
.icon-message {
color: #36a3f7;
}
.icon-money {
color: #f4516c;
}
.icon-shopping {
color: #34bfa3
}
.card-panel-icon-wrapper {
float: left;
margin: 14px 0 0 14px;
padding: 16px;
transition: all 0.38s ease-out;
border-radius: 6px;
}
.card-panel-icon {
float: left;
font-size: 48px;
}
.card-panel-description {
float: right;
font-weight: bold;
margin: 26px;
margin-left: 0px;
.card-panel-text {
line-height: 18px;
color: rgba(0, 0, 0, 0.45);
font-size: 16px;
margin-bottom: 12px;
}
.card-panel-num {
font-size: 20px;
}
}
}
}
@media (max-width:550px) {
.card-panel-description {
display: none;
}
.card-panel-icon-wrapper {
float: none !important;
width: 100%;
height: 100%;
margin: 0 !important;
.svg-icon {
display: block;
margin: 14px auto !important;
float: none !important;
}
}
}
</style>

View File

@@ -0,0 +1,55 @@
import { debounce } from '@/utils'
export default {
data() {
return {
$_sidebarElm: null,
$_resizeHandler: null
}
},
mounted() {
this.$_resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
beforeDestroy() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
// to fixed bug when cached by keep-alive
// https://github.com/PanJiaChen/vue-element-admin/issues/2116
activated() {
this.$_initResizeEvent()
this.$_initSidebarResizeEvent()
},
deactivated() {
this.$_destroyResizeEvent()
this.$_destroySidebarResizeEvent()
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_initResizeEvent() {
window.addEventListener('resize', this.$_resizeHandler)
},
$_destroyResizeEvent() {
window.removeEventListener('resize', this.$_resizeHandler)
},
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.$_resizeHandler()
}
},
$_initSidebarResizeEvent() {
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
},
$_destroySidebarResizeEvent() {
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
}
}
}

View File

@@ -0,0 +1,166 @@
<template>
<div>
<el-dialog title="添加云打印机" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-form-item label="层级">
<el-select v-model="form.contentType">
<el-option :label="item.name" :value="item.value" v-for="item in devices"
:key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备尺寸">
<el-radio-group v-model="form.config.width">
<el-radio-button label="58">58mm</el-radio-button>
<el-radio-button label="80">80mm</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="设备名称" prop="name">
<el-input v-model="form.name" placeholder="请输入设备名称"></el-input>
</el-form-item>
<el-form-item label="设备号" prop="address">
<el-input v-model="form.address" placeholder="请输入设备号"></el-input>
</el-form-item>
<el-form-item label="打印份数">
<el-select v-model="form.config.printerNum">
<el-option :label="item" :value="item" v-for="item in 4" :key="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="出品模式">
<el-select v-model="form.config.model">
<el-option :label="item.name" :value="item.value" v-for="item in models"
:key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="打印类型">
<el-select v-model="form.subType">
<el-option :label="item.name" :value="item.value" v-for="item in subTypes"
:key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="尾部留空">
<el-radio-group v-model="form.config.feet">
<el-radio-button :label="`${item}`" v-for="item in feets" :key="item">{{ item
}}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="自动切刀">
<el-switch v-model="form.config.autoCut" :active-value="0" :inactive-value="1"></el-switch>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="form.status" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort" controls-position="right" :min="0"></el-input-number>
</el-form-item>
<el-form-item label="商品分类">
<div style="cursor: pointer;" @click="$refs.classify.show()">
<span style="color: #409eff;" v-for="item in form.config.categoryList">{{ item.name }},</span>
<span style="color: #e65d6e;" v-if="!form.config.categoryList.length">请选择分类</span>
</div>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
<classify ref="classify" @success="classifySuccess" />
</div>
</template>
<script>
import { devices, models, subTypes } from '../devices'
import { tbPrintMachine } from '@/api/devices'
import classify from '@/components/classify'
export default {
components: { classify },
data() {
return {
dialogVisible: false,
devices,
models,
subTypes,
feets: [0, 1, 2, 3, 4, 5, 8],
loading: false,
form: {
id: '',
contentType: '',
config: {
width: '80mm', // 设备尺寸
printerNum: 1, //打印份数
categoryList: '', // 商品分类
model: 'normal', // 出品模式,
feet: '0',
autoCut: 0
},
name: '',
subType: 'kitchen', // 打印类型
status: 0,
sort: ''
},
resetForm: '',
rules: {
name: [
{
required: true,
message: ' ',
trigger: 'blur'
}
],
address: [
{
required: true,
message: ' ',
trigger: 'blur'
}
]
}
}
},
mounted() {
this.resetForm = { ...this.form }
},
methods: {
// 确认选择商品分类
classifySuccess(e) {
this.form.config.categoryList = e
},
onSubmitHandle() {
console.log(this.form)
this.$refs.form.validate(async valid => {
if (valid) {
try {
this.loading = true
this.form.shopId = localStorage.getItem('shopId')
let res = await tbPrintMachine(this.form, this.form.id ? 'put' : 'post')
this.$emit('success', res)
this.close()
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
this.loading = false
} catch (error) {
this.loading = false
console.log(error)
}
}
})
},
show(obj) {
this.dialogVisible = true
if (obj && obj.id) {
this.form = { ...obj }
}
},
close() {
this.dialogVisible = false
},
reset() {
this.form = { ...this.resetForm }
}
}
}
</script>

View File

@@ -0,0 +1,44 @@
export const devices = [
{
value: 'printer',
name: '本地'
},
{
value: 'yxyPrinter',
name: '云想印'
},
{
value: 'fePrinter',
name: '飞鹅'
}
]
export const models = [
{
value: 'normal',
name: '普通出单'
},
{
value: 'one',
name: '一菜一品'
},
{
value: 'category',
name: '分类出单'
}
]
export const subTypes = [
{
value: 'kitchen',
name: '出品'
},
{
value: 'cash',
name: '小票'
},
{
value: 'label',
name: '标签'
}
]

View File

@@ -0,0 +1,159 @@
<template>
<div class="app-container">
<div class="head-container">
<el-form :model="query" inline>
<el-form-item>
<el-input v-model="query.name" placeholder="请输入设备名称"></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="query.type" placeholder="请选择设备类型">
<el-option :label="item.name" :value="item.value" v-for="item in devices" :key="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addDevice.show()">
添加云打印机
</el-button>
</div>
<div class="head-container">
<el-table :data="tableData.data" v-loading="tableData.loading">
<el-table-column label="设备名称" prop="name"></el-table-column>
<el-table-column label="设备号" prop="address"></el-table-column>
<el-table-column label="品牌" prop="contentType">
<template v-slot="scope">
{{ scope.row.contentType | devicesName }}
</template>
</el-table-column>
<el-table-column label="出品模式" prop="config.model">
<template v-slot="scope">
{{ scope.row.config.model | modelsName }}
</template>
</el-table-column>
<el-table-column label="打印类型" prop="subType">
<template v-slot="scope">
{{ scope.row.subType | subTypesName }}
</template>
</el-table-column>
<el-table-column label="创建时间" sortable prop="createdAt">
<template v-slot="scope">
{{ scope.row.createdAt | timeFilter }}
</template>
</el-table-column>
<el-table-column label="排序" sortable prop="sort"></el-table-column>
<el-table-column label="状态" prop="status">
<template v-slot="scope">
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0"
@change="statusChange($event, scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template v-slot="scope">
<el-button type="text" icon="el-icon-edit"
@click="$refs.addDevice.show(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="delTableHandle([scope.row.id])">
<el-button type="text" icon="el-icon-delete" style="margin-left: 20px !important;"
slot="reference">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total"></el-pagination>
</div>
<addDevice ref="addDevice" @success="getTableData" />
</div>
</template>
<script>
import { devices, models, subTypes } from './devices'
import addDevice from './components/addDevice'
import { tbPrintMachineGet, tbPrintMachine } from '@/api/devices'
import dayjs from 'dayjs'
export default {
components: {
addDevice
},
data() {
return {
query: {
name: '',
type: ''
},
devices,
tableData: {
data: [],
page: 0,
size: 10,
loading: false,
total: 0
}
}
},
filters: {
devicesName(value) {
return devices.find(item => item.value == value).name
},
modelsName(value) {
return models.find(item => item.value == value).name
},
subTypesName(value) {
return subTypes.find(item => item.value == value).name
},
timeFilter(s) {
return dayjs(s).format('YYYY-MM-DD HH:mm:ss')
}
},
mounted() {
this.getTableData()
},
methods: {
// 切换状态
async statusChange(e, row) {
try {
this.tableData.loading = true
const data = { ...row }
data.status = e
await tbPrintMachine(data, 'put')
this.getTableData()
} catch (error) {
console.log(error)
this.tableData.loading = false
}
},
// 重置查询
resetHandle() {
this.query.name = ''
this.query.type = ''
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbPrintMachineGet({
name: this.query.name,
contentType: this.query.type
})
this.tableData.loading = false
this.tableData.data = res
this.tableData.total = res.length
} catch (error) {
console.log(error)
}
}
}
}
</script>

View File

@@ -0,0 +1,89 @@
<template>
<div class="errPage-container">
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
返回
</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">
Oops!
</h1>
<h2>你没有权限去该页面</h2>
<h6>如有不满请联系你领导</h6>
<ul class="list-unstyled">
<li>或者你可以去:</li>
<li class="link-type">
<router-link to="/dashboard">
回首页
</router-link>
</li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
</div>
</template>
<script>
import errGif from '@/assets/401_images/401.gif'
export default {
name: 'Page401',
data() {
return {
errGif: errGif + '?' + +new Date()
}
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/dashboard' })
} else {
this.$router.go(-1)
}
}
}
}
</script>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none!important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

225
src/views/features/404.vue Normal file
View File

@@ -0,0 +1,225 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确请点击以下按钮返回主页或者发送错误报告</div>
<a href="/" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Page404',
computed: {
message() {
return '网管说这个页面你不能进......'
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@@ -0,0 +1,12 @@
<script>
export default {
created() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
</script>

View File

@@ -0,0 +1,325 @@
<template>
<div class="app-container">
<el-row :gutter="15">
<el-col style="margin-bottom: 10px">
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span class="role-span">字段配置{{ tableName }}</span>
<el-button
:loading="genLoading"
icon="el-icon-s-promotion"
size="mini"
style="float: right; padding: 6px 9px;"
type="success"
@click="toGen"
>保存&生成</el-button>
<el-button
:loading="columnLoading"
icon="el-icon-check"
size="mini"
style="float: right; padding: 6px 9px;margin-right: 9px"
type="primary"
@click="saveColumnConfig"
>保存</el-button>
<el-tooltip class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
<el-button
:loading="syncLoading"
icon="el-icon-refresh"
size="mini"
style="float: right; padding: 6px 9px;"
type="info"
@click="sync"
>同步</el-button>
</el-tooltip>
</div>
<el-form size="small" label-width="90px">
<el-table v-loading="loading" :data="data" :max-height="tableHeight" size="small" style="width: 100%;margin-bottom: 15px">
<el-table-column prop="columnName" label="字段名称" />
<el-table-column prop="columnType" label="字段类型" />
<el-table-column prop="remark" label="字段描述">
<template slot-scope="scope">
<el-input v-model="data[scope.$index].remark" size="mini" class="edit-input" />
</template>
</el-table-column>
<el-table-column align="center" label="必填" width="70px">
<template slot-scope="scope">
<el-checkbox v-model="data[scope.$index].notNull" />
</template>
</el-table-column>
<el-table-column align="center" label="列表" width="70px">
<template slot-scope="scope">
<el-checkbox v-model="data[scope.$index].listShow" />
</template>
</el-table-column>
<el-table-column align="center" label="表单" width="70px">
<template slot-scope="scope">
<el-checkbox v-model="data[scope.$index].formShow" />
</template>
</el-table-column>
<el-table-column label="表单类型">
<template slot-scope="scope">
<el-select v-model="data[scope.$index].formType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
<el-option
label="文本框"
value="Input"
/>
<el-option
label="文本域"
value="Textarea"
/>
<el-option
label="单选框"
value="Radio"
/>
<el-option
label="下拉框"
value="Select"
/>
<el-option
label="日期框"
value="Date"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="查询方式">
<template slot-scope="scope">
<el-select v-model="data[scope.$index].queryType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
<el-option
label="="
value="="
/>
<el-option
label="!="
value="!="
/>
<el-option
label=">="
value=">="
/>
<el-option
label="<="
value="<="
/>
<el-option
label="Like"
value="Like"
/>
<el-option
label="NotNull"
value="NotNull"
/>
<el-option
label="BetWeen"
value="BetWeen"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="日期注解">
<template slot-scope="scope">
<el-select v-model="data[scope.$index].dateAnnotation" filterable class="edit-input" clearable size="mini" placeholder="请选择">
<el-option
label="自动创建时间"
value="CreationTimestamp"
/>
<el-option
label="自动更新时间"
value="UpdateTimestamp"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="关联字典">
<template slot-scope="scope">
<el-select v-model="data[scope.$index].dictName" filterable class="edit-input" clearable size="mini" placeholder="请选择">
<el-option v-for="item in dicts" :key="item.id" :label="item.remark === '' ? item.name : item.remark" :value="item.name" />
</el-select>
</template>
</el-table-column>
</el-table>
</el-form>
</el-card>
</el-col>
<el-col>
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span class="role-span">生成配置</span>
<el-button
:loading="configLoading"
icon="el-icon-check"
size="mini"
style="float: right; padding: 6px 9px"
type="primary"
@click="doSubmit"
>保存</el-button>
</div>
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="78px">
<el-form-item label="作者名称" prop="author">
<el-input v-model="form.author" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">类上面的作者名称</span>
</el-form-item>
<el-form-item label="模块名称" prop="moduleName">
<el-input v-model="form.moduleName" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">模块的名称请选择项目中已存在的模块</span>
</el-form-item>
<el-form-item label="至于包下" prop="pack">
<el-input v-model="form.pack" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">项目包的名称生成的代码放到哪个包里面</span>
</el-form-item>
<el-form-item label="接口名称" prop="apiAlias">
<el-input v-model="form.apiAlias" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">接口的名称用于控制器与接口文档中</span>
</el-form-item>
<el-form-item label="前端路径" prop="path">
<el-input v-model="form.path" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">输入views文件夹下的目录不存在即创建</span>
</el-form-item>
<!-- <el-form-item label="接口目录">-->
<!-- <el-input v-model="form.apiPath" style="width: 40%" />-->
<!-- <span style="color: #C0C0C0;margin-left: 10px;">Api存放路径[src/api]为空则自动生成路径</span>-->
<!-- </el-form-item>-->
<el-form-item label="去表前缀" prop="prefix">
<el-input v-model="form.prefix" placeholder="默认不去除表前缀" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">默认不去除表前缀可自定义</span>
</el-form-item>
<el-form-item label="是否覆盖" prop="cover">
<el-radio-group v-model="form.cover" size="mini" style="width: 40%">
<el-radio-button label="true"></el-radio-button>
<el-radio-button label="false"></el-radio-button>
</el-radio-group>
<span style="color: #C0C0C0;margin-left: 10px;">谨防误操作请慎重选择</span>
</el-form-item>
</el-form>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import crud from '@/mixins/crud'
import { update, get } from '@/api/generator/genConfig'
import { save, sync, generator } from '@/api/generator/generator'
import { getDicts } from '@/api/system/dict'
export default {
name: 'GeneratorConfig',
components: {},
mixins: [crud],
data() {
return {
activeName: 'first', tableName: '', tableHeight: 550, columnLoading: false, configLoading: false, dicts: [], syncLoading: false, genLoading: false,
form: { id: null, tableName: '', author: '', pack: '', path: '', moduleName: '', cover: 'false', apiPath: '', prefix: '', apiAlias: null },
rules: {
author: [
{ required: true, message: '作者不能为空', trigger: 'blur' }
],
pack: [
{ required: true, message: '包路径不能为空', trigger: 'blur' }
],
moduleName: [
{ required: true, message: '包路径不能为空', trigger: 'blur' }
],
path: [
{ required: true, message: '前端路径不能为空', trigger: 'blur' }
],
apiAlias: [
{ required: true, message: '接口名称不能为空', trigger: 'blur' }
],
cover: [
{ required: true, message: '不能为空', trigger: 'blur' }
]
}
}
},
created() {
this.tableHeight = document.documentElement.clientHeight - 385
this.tableName = this.$route.params.tableName
this.$nextTick(() => {
this.init()
get(this.tableName).then(data => {
this.form = data
this.form.cover = this.form.cover.toString()
})
getDicts().then(data => {
this.dicts = data
})
})
},
methods: {
beforeInit() {
this.url = 'api/generator/columns'
const tableName = this.tableName
this.params = { tableName }
return true
},
saveColumnConfig() {
this.columnLoading = true
save(this.data).then(res => {
this.notify('保存成功', 'success')
this.columnLoading = false
}).catch(err => {
this.columnLoading = false
console.log(err.response.data.message)
})
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.configLoading = true
update(this.form).then(res => {
this.notify('保存成功', 'success')
this.form = res
this.form.cover = this.form.cover.toString()
this.configLoading = false
}).catch(err => {
this.configLoading = false
console.log(err.response.data.message)
})
}
})
},
sync() {
this.syncLoading = true
sync([this.tableName]).then(() => {
this.init()
this.notify('同步成功', 'success')
this.syncLoading = false
}).then(() => {
this.syncLoading = false
})
},
toGen() {
this.genLoading = true
save(this.data).then(res => {
this.notify('保存成功', 'success')
// 生成代码
generator(this.tableName, 0).then(data => {
this.genLoading = false
this.notify('生成成功', 'success')
}).catch(err => {
this.genLoading = false
console.log(err.response.data.message)
})
}).catch(err => {
this.genLoading = false
console.log(err.response.data.message)
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.edit-input {
.el-input__inner {
border: 1px solid #e5e6e7;
}
}
</style>
<style scoped>
::v-deep .input-with-select .el-input-group__prepend {
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,114 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<el-input v-model="query.name" clearable size="small" placeholder="请输入表名" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation />
</div>
<crudOperation>
<el-tooltip slot="right" class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
<el-button
class="filter-item"
size="mini"
type="success"
icon="el-icon-refresh"
:loading="syncLoading"
:disabled="crud.selections.length === 0"
@click="sync"
>同步</el-button>
</el-tooltip>
</crudOperation>
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="tableName" label="表名" />
<el-table-column :show-overflow-tooltip="true" prop="engine" label="数据库引擎" />
<el-table-column :show-overflow-tooltip="true" prop="coding" label="字符编码集" />
<el-table-column :show-overflow-tooltip="true" prop="remark" label="备注" />
<el-table-column prop="createTime" label="创建日期" />
<el-table-column label="操作" width="160px" align="center" fixed="right">
<template slot-scope="scope">
<el-button size="mini" style="margin-right: 2px" type="text">
<router-link :to="'/sys-tools/generator/preview/' + scope.row.tableName">
预览
</router-link>
</el-button>
<el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text" @click="toDownload(scope.row.tableName)">下载</el-button>
<el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text">
<router-link :to="'/sys-tools/generator/config/' + scope.row.tableName">
配置
</router-link>
</el-button>
<el-button type="text" style="margin-left: -1px" size="mini" @click="toGen(scope.row.tableName)">生成</el-button>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import { generator, sync } from '@/api/generator/generator'
import { downloadFile } from '@/utils/index'
import CRUD, { presenter, header } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
export default {
name: 'GeneratorIndex',
components: { pagination, crudOperation, rrOperation },
cruds() {
return CRUD({ url: 'api/generator/tables' })
},
mixins: [presenter(), header()],
data() {
return {
syncLoading: false
}
},
created() {
this.crud.optShow = { add: false, edit: false, del: false, download: false }
},
methods: {
toGen(tableName) {
// 生成代码
generator(tableName, 0).then(data => {
this.$notify({
title: '生成成功',
type: 'success',
duration: 2500
})
})
},
toDownload(tableName) {
// 打包下载
generator(tableName, 2).then(data => {
downloadFile(data, tableName, 'zip')
})
},
sync() {
const tables = []
this.crud.selections.forEach(val => {
tables.push(val.tableName)
})
this.syncLoading = true
sync(tables).then(() => {
this.crud.refresh()
this.crud.notify('同步成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
this.syncLoading = false
}).then(() => {
this.syncLoading = false
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,30 @@
<template>
<el-tabs v-model="activeName" type="card">
<el-tab-pane v-for="item in data" :key="item.name" :lazy="true" :label="item.name" :name="item.name">
<Java :value="item.content" :height="height" />
</el-tab-pane>
</el-tabs>
</template>
<script>
import Java from '@/components/JavaEdit/index'
import { generator } from '@/api/generator/generator'
export default {
name: 'Preview',
components: { Java },
data() {
return {
data: null, height: '', activeName: 'Entity'
}
},
created() {
this.height = document.documentElement.clientHeight - 180 + 'px'
const tableName = this.$route.params.tableName
generator(tableName, 1).then(data => {
this.data = data
}).catch(() => {
this.$router.go(-1)
})
}
}
</script>

View File

@@ -0,0 +1,381 @@
<template>
<div class="app-container">
<el-tabs v-model="orderType" @tab-click="getTableData">
<el-tab-pane label="收款" name="1"></el-tab-pane>
<el-tab-pane label="销量" name="2"></el-tab-pane>
</el-tabs>
<div class="head-container">
<el-form :model="query" label-position="left">
<el-form-item>
<el-radio-group v-model="timeValue" @change="timeChange">
<el-radio-button label="">全部</el-radio-button>
<el-radio-button label="0">今天</el-radio-button>
<el-radio-button label="-1">昨天</el-radio-button>
<el-radio-button label="-7">最近7天</el-radio-button>
<el-radio-button label="-30">最近30天</el-radio-button>
<el-radio-button label="week">本周</el-radio-button>
<el-radio-button label="month">本月</el-radio-button>
<el-radio-button label="custom">自定义</el-radio-button>
</el-radio-group>
<el-date-picker
v-model="query.createdAt"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
value-format="yyyy-MM-dd HH:mm:ss"
v-if="timeValue == 'custom'"
>
</el-date-picker
><el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
<el-button
icon="el-icon-download"
v-loading="downloadLoading"
@click="downloadHandle"
>
<span v-if="!downloadLoading">导出Excel</span>
<span v-else>下载中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<div class="collect_wrap">
<div class="item" v-for="item in payCountList" :key="item.id">
<div class="icon_wrap" style="--bg-color:#C978EE">
<i class="icon" :class="item.icon"></i>
</div>
<div class="info">
<div class="m">{{ item.payAmount }}</div>
<div class="t">{{ item.payType }}</div>
</div>
</div>
</div>
</div>
<div class="head-container">
<el-table
:data="tableData.data"
v-loading="tableData.loading"
v-if="orderType == 1"
>
<el-table-column label="日期" prop="tradeDay"></el-table-column>
<el-table-column label="总金额" prop="total"></el-table-column>
<el-table-column label="微信小程序支付" prop="wxLite"></el-table-column>
<el-table-column label="扫码支付金额" prop="scanCode"></el-table-column>
<el-table-column label="现金支付金额" prop="cash"></el-table-column>
</el-table>
<el-table
:data="tableData.data"
v-loading="tableData.loading"
v-if="orderType == 2"
>
<el-table-column label="商品名称" prop="productName"></el-table-column>
<el-table-column
label="商品描述"
prop="productSkuName"
></el-table-column>
<el-table-column label="销量" prop="salesNum"></el-table-column>
<el-table-column label="退单量" prop="refNum"></el-table-column>
<el-table-column label="总量" prop="num"></el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination
:total="tableData.total"
:current-page="tableData.page + 1"
:page-size="tableData.size"
@current-change="paginationChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
</div>
</div>
</template>
<script>
import { daydownload, daycount, summaryday } from "@/api/home";
import dayjs from "dayjs";
import { downloadFile } from "@/utils/index";
export default {
data() {
return {
timeValue: "",
resetQuery: null,
orderType: "1",
query: {
createdAt: []
},
tableData: {
data: [],
page: 0,
size: 10,
loading: false,
total: 0
},
downloadLoading: false,
payCountList: "",
payCountTotal: 0
};
},
filters: {
timeFilter(time) {
return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
}
},
mounted() {
this.resetQuery = { ...this.query };
this.getTableData();
},
methods: {
// 获取订单汇总
async daycount() {
try {
const res = await daycount({
startTime: this.query.createdAt[0],
endTime: this.query.createdAt[1]
});
this.payCountList = res;
} catch (error) {
console.log(error);
}
},
// 导出Excel
async downloadHandle() {
try {
this.downloadLoading = true;
const file = await daydownload({
type: this.orderType,
startTime: this.query.createdAt[0],
endTime: this.query.createdAt[1]
});
downloadFile(file, "数据", "xlsx");
this.downloadLoading = false;
} catch (error) {
this.downloadLoading = false;
console.log(error);
}
},
// 重置查询
resetHandle() {
this.timeValue = "";
this.query = { ...this.resetQuery };
this.page = 0;
this.getTableData();
},
// 分页大小改变
sizeChange(e) {
this.tableData.size = e;
this.getTableData();
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1;
this.getTableData();
},
async getTableData() {
this.tableData.loading = true;
try {
this.daycount();
const res = await summaryday({
page: this.tableData.page,
size: this.tableData.size,
type: this.orderType,
startTime: this.query.createdAt[0],
endTime: this.query.createdAt[1]
});
this.tableData.loading = false;
this.tableData.data = res.content;
this.tableData.total = res.totalElements;
} catch (error) {
console.log(error);
}
},
// 切换时间
timeChange(e) {
const format = ["YYYY-MM-DD 00:00:00", "YYYY-MM-DD 23:59:59"];
switch (e) {
case "":
// 全部
this.query.createdAt = [];
break;
case "0":
// 今天
this.query.createdAt = [
dayjs().format(format[0]),
dayjs().format(format[1])
];
break;
case "-1":
// 昨天
this.query.createdAt = [
dayjs()
.add(-1, "d")
.format(format[0]),
dayjs()
.add(-1, "d")
.format(format[1])
];
break;
case "-7":
// 最近7天
this.query.createdAt = [
dayjs()
.add(-7, "d")
.format(format[0]),
dayjs().format(format[1])
];
break;
case "-30":
// 最近7天
this.query.createdAt = [
dayjs()
.add(-30, "d")
.format(format[0]),
dayjs().format(format[1])
];
break;
case "week":
// 本周
this.query.createdAt = [
dayjs()
.startOf("week")
.format(format[0]),
dayjs()
.endOf("week")
.format(format[1])
];
break;
case "month":
// 本周
this.query.createdAt = [
dayjs()
.startOf("month")
.format(format[0]),
dayjs()
.endOf("month")
.format(format[1])
];
break;
case "custom":
// 自定义
this.query.createdAt = [];
break;
default:
break;
}
}
}
};
</script>
<style scoped lang="scss">
.collect_wrap {
display: flex;
gap: 14px;
.item {
flex: 1;
display: flex;
align-items: center;
background-color: #f5f5f5;
padding: 20px;
.icon_wrap {
$size: 34px;
$border: 6px;
width: $size;
height: $size;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--bg-color);
border-radius: 50%;
position: relative;
&::after {
content: "";
width: $size + $border;
height: $size + $border;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--bg-color);
opacity: 0.3;
}
.icon {
font-size: 16px;
color: #fff;
}
.img {
width: 20px;
height: 20px;
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.m {
font-weight: bold;
}
.t {
font-size: 12px;
color: #999;
padding-top: 4px;
}
}
}
}
.refund {
color: #ff9731;
font-weight: bold;
}
.table_order_info {
.order_no {
color: #999;
}
.type {
color: #e6a23c;
}
}
.goods_info {
.row {
display: flex;
&:not(:first-child) {
margin-top: 10px;
}
.cover {
width: 40px;
height: 40px;
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.sku {
color: #999;
}
}
}
}
</style>

899
src/views/home/home.vue Normal file
View File

@@ -0,0 +1,899 @@
<template>
<div class="app-container" style="padding-bottom: 100px;">
<div class="card_wrap">
<div class="card">
<div class="header">
<div class="card_title">总销售额</div>
<el-tooltip effect="dark" content="订单支付金额" placement="top">
<i class="icon el-icon-warning-outline"></i>
</el-tooltip>
</div>
<div class="number">{{ topData.totalSales || 0 }}</div>
<div class="row">平均每单{{ topData.averageSales || 0 }}</div>
<div class="row">今日销售额{{ topData.totalSalesToday || 0 }}</div>
</div>
<div class="card">
<div class="header">
<div class="card_title">支付笔数</div>
</div>
<div class="number">{{ topData.paymentsNumber }}</div>
<div class="row" ref="cardPayChart" style="padding-bottom: 2px;"></div>
<div class="row">
今日支付笔数{{ topData.paymentsNumberToday || 0 }}
</div>
</div>
<div class="card">
<div class="header">
<div class="card_title">访问量</div>
</div>
<div class="number">{{ topData.totalVisits }}</div>
<div class="row" ref="cardCountChart" style="padding-bottom: 2px;"></div>
<div class="row">
<div class="dot"></div>
今日访问 {{ topData.totalVisitsToday || 0 }}
</div>
</div>
<div class="card">
<div class="header">
<div class="card_title">用户数</div>
</div>
<div class="number">{{ topData.totalUser }}</div>
<div class="row" ref="cardUserChart" style="padding-bottom: 2px;"></div>
<div class="row">
今日新增 {{ topData.userToday || 0 }}
<i class="icon el-icon-caret-top"></i>
</div>
</div>
</div>
<!-- 销售额 -->
<div class="chart_wrap">
<div class="item">
<div class="header">
<div class="tab_wrap">
<div class="item active">销售额</div>
</div>
<el-radio-group v-model="saleActive" @change="dateAmount">
<el-radio-button label="7">近7天</el-radio-button>
<el-radio-button label="30">30</el-radio-button>
</el-radio-group>
</div>
<div
class="chart"
ref="saleChart"
v-loading="saleLoading"
style="height: 400px;"
></div>
</div>
</div>
<div class="chart_wrap" style="display: flex;">
<!-- 商品销售排行 -->
<div class="item">
<div class="header">
<div class="tab_wrap">
<div class="item active">商品销售排行</div>
</div>
<el-radio-group v-model="saleTableActive" @change="rankChange">
<el-radio-button label="1">今天</el-radio-button>
<el-radio-button label="7">近7天</el-radio-button>
<el-radio-button label="30">30</el-radio-button>
</el-radio-group>
</div>
<div class="sale_data">
<div class="card">
<div class="sale_data_header">
<div class="card_title">销售数量</div>
</div>
<div class="number">{{ productCount }}</div>
<div class="product_chart_wrap" ref="productCountChart"></div>
</div>
<div class="card">
<div class="sale_data_header">
<div class="card_title">销售金额</div>
</div>
<div class="number">{{ productSum }}</div>
<div class="product_chart_wrap" ref="productSumChart"></div>
</div>
</div>
<div class="table">
<el-table :data="saleTable" v-loading="saleTableLoading">
<el-table-column label="排名" prop="productId"></el-table-column>
<el-table-column
label="商品名称"
prop="productName"
></el-table-column>
<el-table-column label="数量" prop="productNum"></el-table-column>
<el-table-column label="金额" prop="amount"></el-table-column>
</el-table>
<div
class="head-container"
style="padding-top: 20px;display: flex;justify-content: flex-end;"
>
<el-pagination
:total="saleTableTotal"
:page-size="saleTableSize"
:current-page="saleTablePage"
@current-change="paginationChange"
layout="total, prev, pager, next, jumper"
></el-pagination>
</div>
</div>
</div>
<!-- 支付类型占比 -->
<div class="item" style="margin-left: 20px;">
<div class="header">
<div class="tab_wrap">
<div class="item active">支付占比类型</div>
</div>
<el-radio-group v-model="payChartDay" @change="datePayType">
<el-radio-button label="7">近7天</el-radio-button>
<el-radio-button label="30">30</el-radio-button>
</el-radio-group>
</div>
<div
style="height: 400px;margin-top: 30px;"
ref="payChart"
v-loading="payChartLoading"
></div>
</div>
</div>
</div>
</template>
<script>
import {
summaryGet,
summaryTodayGet,
dateProduct,
dateAmount,
datePayType,
summaryDateGet
} from "@/api/home";
import echarts from "echarts";
import { debounce } from "@/utils";
export default {
name: "home",
data() {
return {
topData: "",
saleTab: "sale",
saleActive: "7",
cardPayChart: null,
cardCountChart: null,
cardUserChart: null,
saleLoading: false,
saleChart: null,
payChartDay: "7",
payChartLoading: false,
payChart: null,
chartType: 1,
productCount: 0,
productSum: 0,
saleTableActive: "1",
saleTable: [],
saleTableLoading: false,
saleTablePage: 1,
saleTableTotal: 0,
saleTableSize: 5,
__resizeHandler: null,
productCountChart: null,
productSumChart: null
};
},
mounted() {
this.summaryGet();
this.dateAmount();
this.dateProduct();
this.datePayType();
this.summaryDateGet();
this.__resizeHandler = debounce(() => {
if (this.saleChart) {
this.saleChart.resize();
}
if (this.payChart) {
this.payChart.resize();
}
if (this.cardPayChart) {
this.cardPayChart.resize();
}
if (this.cardUserChart) {
this.cardUserChart.resize();
}
if (this.productCountChart) {
this.productCountChart.resize();
}
if (this.productSumChart) {
this.productSumChart.resize();
}
}, 100);
window.addEventListener("resize", this.__resizeHandler);
this.initCardUserChart();
},
methods: {
// 初始化支付笔数柱状图
initCardPayChart(time = [], data = []) {
this.cardPayChart = echarts.init(this.$refs.cardPayChart);
this.cardPayChart.setOption({
tooltip: {
trigger: "axis"
},
grid: {
x: 0,
y: 0,
x2: 0,
y2: 0
},
xAxis: [
{
type: "category",
data: time,
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
color: "#409eff",
yAxis: [
{
type: "value",
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
series: [
{
data: data,
type: "bar",
barWidth: "30%"
}
]
});
},
// 初始化访问量柱状图
initCardCountChart(time = [], data = []) {
this.cardCountChart = echarts.init(this.$refs.cardCountChart);
this.cardCountChart.setOption({
tooltip: {
trigger: "axis"
},
grid: {
x: 0,
y: 0,
x2: 0,
y2: 0
},
xAxis: [
{
type: "category",
data: time,
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
color: "#409eff",
yAxis: [
{
type: "value",
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
series: [
{
data: data,
type: "bar",
barWidth: "30%"
}
]
});
},
// 初始化用户数折线图
initCardUserChart(time = [], data = []) {
this.cardUserChart = echarts.init(this.$refs.cardUserChart);
this.cardUserChart.setOption({
tooltip: {
trigger: "axis"
},
grid: {
x: 0,
y: 10,
x2: 0,
y2: 2
},
xAxis: [
{
type: "category",
data: time,
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
color: "#409eff",
yAxis: [
{
type: "value",
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
series: [
{
data: data,
type: "line",
symbol: "none"
}
]
});
},
// 初始化销售额图标
initSaleChart(time, data) {
this.saleChart = null;
this.saleChart = echarts.init(this.$refs.saleChart);
this.saleChart.setOption({
title: {
text: "销售趋势",
x: "center"
},
tooltip: {
trigger: "axis"
},
xAxis: [
{
type: "category",
data: time,
axisTick: {
alignWithLabel: true
},
axisLine: {
lineStyle: {
color: "#999"
}
},
axisLabel: {
rotate: time.length <= 7 ? 0 : 45,
interval: 0,
textStyle: {
fontSize: "9"
}
}
}
],
color: "#409eff",
yAxis: [
{
type: "value",
axisLine: {
lineStyle: {
color: "#999"
}
},
splitLine: {
lineStyle: {
type: "dashed",
color: "#ececec"
}
}
}
],
series: [
{
data: data,
type: "bar",
barWidth: time.length <= 7 ? "50%" : "30%"
}
]
});
},
// 初始化销售额图表
initPayChart(data) {
this.payChart = echarts.init(this.$refs.payChart);
this.payChart.setOption({
tooltip: {
trigger: "item"
},
legend: {
top: "5%",
left: "center"
},
color: [
"#409eff",
"#91cc75",
"#fac858",
"#ee6666",
"#73c0de",
"#3ba272",
"#fc8452",
"#9a60b4",
"#ea7ccc"
],
series: [
{
type: "pie",
radius: ["40%", "70%"],
avoidLabelOverlap: false,
label: {
show: false,
position: "center"
},
emphasis: {
label: {
show: true,
fontSize: 20
}
},
labelLine: {
show: false
},
data: data
}
]
});
},
// 获取销售额柱状图数据
async dateAmount() {
try {
this.saleLoading = true;
const res = await dateAmount(this.saleActive);
const data = res.total.map(item => item.amount);
const time = res.total.map(item => item.tradeDay);
this.initSaleChart(time, data);
setTimeout(() => {
this.saleLoading = false;
}, 300);
} catch (error) {
console.log(error);
}
},
paginationChange(e) {
this.saleTablePage = e;
this.dateProduct();
},
// 获取销售额排行表格数据
async dateProduct() {
try {
this.saleTableLoading = true;
const res = await dateProduct(
this.saleTableActive,
this.saleTablePage,
this.saleTableSize
);
this.saleTable = res.totalProduct;
this.saleTableTotal = res.total;
this.productCount = res.productCount;
this.productSum = res.productSum;
setTimeout(() => {
this.saleTableLoading = false;
}, 300);
} catch (error) {
console.log(error);
}
},
// 支付类型占比 饼图
async datePayType() {
try {
this.payChartLoading = true;
const res = await datePayType(this.payChartDay);
const data = res.countPayType.map(item => {
return {
value: item.count,
name: item.payType
};
});
setTimeout(() => {
this.payChartLoading = false;
}, 300);
this.initPayChart(data);
} catch (error) {
console.log(error);
}
},
// 汇总数据
async summaryGet() {
try {
const res1 = await summaryGet();
const res2 = await summaryTodayGet();
this.topData = {
...res1,
...res2
};
let payTime = res1.countDateList.map(item => item.tradeDay);
let payData = res1.countDateList.map(item => item.count);
let countTime = res1.visitsCountList.map(item => item.tradeDay);
let countData = res1.visitsCountList.map(item => item.count);
this.initCardPayChart(payTime, payData);
this.initCardCountChart(countTime, countData);
console.log(this.topData);
} catch (error) {
console.log(error);
}
},
rankChange() {
this.dateProduct();
this.summaryDateGet();
},
// 初始化销售图标
initProduceChart(p1, p2) {
this.productCountChart = echarts.init(this.$refs.productCountChart);
this.productSumChart = echarts.init(this.$refs.productSumChart);
this.productCountChart.setOption({
tooltip: {
trigger: "axis"
},
grid: {
x: 0,
y: 0,
x2: 0,
y2: 0
},
xAxis: [
{
boundaryGap: false,
type: "category",
data: p1[0],
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
color: "#409eff",
yAxis: [
{
type: "value",
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
series: [
{
data: p1[1],
type: "line",
symbol: "none",
smooth: true,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#409eff" // 渐变颜色
},
{
offset: 1,
color: "#409eff" // 渐变颜色
}
])
}
}
]
});
this.productSumChart.setOption({
tooltip: {
trigger: "axis"
},
grid: {
x: 0,
y: 0,
x2: 0,
y2: 0
},
xAxis: [
{
boundaryGap: false,
type: "category",
data: p2[0],
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
color: "#409eff",
yAxis: [
{
type: "value",
show: false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字
axisTick: {
show: false // 不显示坐标轴刻度线
},
axisLine: {
show: false // 不显示坐标轴线
},
axisLabel: {
show: false // 不显示坐标轴上的文字
},
splitLine: {
show: false // 不显示网格线
}
}
],
series: [
{
data: p2[1],
type: "line",
symbol: "none",
smooth: true,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#409eff" // 渐变颜色
},
{
offset: 1,
color: "#409eff" // 渐变颜色
}
])
}
}
]
});
},
// 日期汇总数据
async summaryDateGet() {
try {
const res = await summaryDateGet(this.saleTableActive);
let p1 = [
res.numList.map(item => item.tradeDay),
res.numList.map(item => item.count)
];
let p2 = [
res.amountList.map(item => item.tradeDay),
res.amountList.map(item => item.count)
];
this.initProduceChart(p1, p2);
} catch (error) {
console.log(error);
}
}
}
};
</script>
<style scoped lang="scss">
.app-container {
padding: 20px;
background-color: #f5f5f5;
}
.card_wrap {
display: flex;
flex-wrap: wrap;
gap: 20px;
.card {
flex: 1;
background-color: #fff;
border-radius: 2px;
padding: 0 20px;
.header {
display: flex;
justify-content: space-between;
color: #999;
padding-top: 20px;
.card_title {
font-size: 14px;
flex: 1;
}
}
.number {
padding: 20px 0 10px 0;
font-size: 24px;
height: 60px;
}
.row {
height: 50px;
color: #555;
font-size: 14px;
display: flex;
align-items: center;
&:not(:last-child) {
border-bottom: 1px solid #ececec;
}
.icon {
color: rgb(255, 85, 85);
margin-left: 4px;
}
.dot {
$size: 6px;
width: $size;
height: $size;
border-radius: 50%;
background-color: #1890ff;
margin-right: 6px;
}
}
}
}
.chart_wrap {
margin-top: 20px;
.sale_data {
display: flex;
.card {
flex: 1;
background-color: #fff;
border-radius: 2px;
padding: 0 20px;
.sale_data_header {
display: flex;
justify-content: space-between;
color: #999;
padding-top: 20px;
.card_title {
font-size: 14px;
flex: 1;
}
}
.number {
padding-top: 10px;
font-size: 24px;
height: 60px;
}
.product_chart_wrap {
height: 50px;
}
}
}
.item {
flex: 1;
background-color: #fff;
border-radius: 2px;
.header {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #ececec;
padding: 0 20px;
.tab_wrap {
display: flex;
$color: #1890ff;
.item {
padding: 0 10px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: $color;
&.active {
position: relative;
&::after {
content: "";
width: 100%;
border-bottom: 2px solid $color;
position: absolute;
bottom: 0;
left: 0;
}
}
}
}
}
.chart {
padding: 20px 0;
height: 300px;
}
.table {
padding: 20px;
}
}
}
</style>

View File

@@ -0,0 +1,93 @@
<template>
<el-dialog :title="`${form.id ? '编辑' : '添加'}供应商`" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-form-item label="供应商" prop="purveyorName">
<el-input v-model="form.purveyorName" placeholder="请输入供应商名称"></el-input>
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="form.purveyorTelephone" placeholder="请输入联系电话"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input type="textarea" v-model="form.address" placeholder="请输入地址"></el-input>
</el-form-item>
<el-form-item label="标签">
<el-input v-model="form.tip" placeholder="请输入标签"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
</template>
<script>
import { tbShopPurveyor } from '@/api/invoicing'
export default {
data() {
return {
dialogVisible: false,
form: {
id: '',
purveyorName: '',
purveyorTelephone: '',
address: '',
tip: '',
remark: '',
},
rules: {
purveyorName: [
{
required: true,
message: '请输入供应商名称',
trigger: 'blur'
}
]
}
}
},
methods: {
onSubmitHandle() {
this.$refs.form.validate(async valid => {
if (valid) {
try {
let res = await tbShopPurveyor({
...this.form,
shopId: localStorage.getItem('shopId')
}, this.form.id ? 'put' : 'post')
this.$emit('success', res)
this.close()
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
} catch (error) {
console.log(error)
}
}
})
},
show(obj) {
this.dialogVisible = true
if (obj && obj.id) {
this.form = JSON.parse(JSON.stringify(obj))
}
},
close() {
this.dialogVisible = false
},
reset() {
this.form.id = ''
this.form.purveyorName = ''
this.form.purveyorTelephone = ''
this.form.address = ''
this.form.tip = ''
this.form.remark = ''
}
}
}
</script>

View File

@@ -0,0 +1,178 @@
<!-- 进销存详情记录 -->
<template>
<el-dialog title="详情记录" width="80%" :visible.sync="dialogVisible" @close="dialogVisible = false">
<div class="head-container">
<el-select v-model="query.type" placeholder="选择类型">
<el-option :label="item.label" :value="item.value" v-for="item in typeList" :key="item.id"></el-option>
</el-select>
<el-date-picker v-model="query.createdAt" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
<div class="head-container">
<div class="exchange_wrap">
<div class="item">
<span>{{ exchange || 0 }}</span>
<span>变动数量</span>
</div>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="变动数量" prop="stockNumber">
<template v-slot="scope">
<span class="num" :class="{ active: scope.row.stockNumber > 0 }">{{ scope.row.stockNumber }} {{
scope.row.unitName
}}</span>
</template>
</el-table-column>
<el-table-column label="类型">
<template v-slot="scope">
<el-tag type="info">{{ scope.row.tagName }}</el-tag>
</template>
</el-table-column>
<el-table-column label="剩余库存">
<template v-slot="scope">
{{ scope.row.leftNumber - scope.row.stockNumber }} {{ scope.row.unitName }}
</template>
</el-table-column>
<el-table-column label="操作时间" prop="updatedAt">
<template v-slot="scope">
{{ dayjs(scope.row.updatedAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</el-dialog>
</template>
<script>
import dayjs from 'dayjs'
import { dictDetail, tbProductStockDetail, tbProductStockDetailSum } from '@/api/invoicing'
export default {
data() {
return {
dayjs,
dialogVisible: false,
typeList: [],
query: {
type: '',
createdAt: []
},
goods: '',
tableData: {
page: 0,
size: 10,
total: 0,
sort: 'id',
loading: false,
list: []
},
exchange: 0
}
},
methods: {
async getTableData() {
this.tableData.loading = true
try {
const res = await tbProductStockDetail({
page: this.tableData.page,
size: this.tableData.size,
sort: this.tableData.sort,
shopId: localStorage.getItem('shopId'),
productId: this.goods.id,
type: this.query.type,
createdAt: this.query.createdAt
})
this.tableData.loading = false
this.tableData.list = res.content.map(item => {
item.tagName = this.typeList.find(val => val.value == item.type).label
return item
})
console.log(this.tableData.list)
this.tableData.total = res.totalElements
} catch (error) {
console.log(error);
}
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 重置查询
resetHandle() {
this.query.blurry = ''
this.tableData.page = 0
this.tableData.list = []
this.getTableData()
},
async show(obj) {
this.dialogVisible = true
this.goods = obj
this.tbProductStockDetailSum()
await this.dictDetail()
await this.getTableData()
},
async tbProductStockDetailSum() {
try {
const { exchange } = await tbProductStockDetailSum({
productId: this.goods.id
})
this.exchange = exchange
} catch (error) {
console.log(error);
}
},
async dictDetail() {
try {
const res = await dictDetail({
dictName: 'product_stock_type',
page: 0,
size: 100
})
this.typeList = res.content
} catch (error) {
console.log(error)
}
}
}
}
</script>
<style scoped lang="scss">
.head-container {
display: flex;
align-items: center;
gap: 10px;
}
.num {
color: #67C23A;
&.active {
color: #F56C6C;
}
}
.exchange_wrap {
display: flex;
padding: 20px 30px;
.item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
span:nth-child(1) {
padding-bottom: 10px;
font-size: 24px;
}
}
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<el-dialog title="详情" width="80%" :visible.sync="dialogVisible" @close="dialogVisible = false">
<div class="head-container">
<span>{{ tableData.detail.type == 'reject' ? '退货出库' : '供应商入库' }}</span>
</div>
<div class="head-container">
<el-table :data="tableData.detail.stockSnap" v-loading="tableData.loading" height="400px">
<el-table-column :label="`商品名称${tableData.detail.stockSnap.length}`" prop="name"></el-table-column>
<el-table-column label="变动数量" prop="number">
<template v-slot="scope">
{{ scope.row.number }}{{ scope.row.unitName }}
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<div class="row">备注{{ tableData.detail.remark }}</div>
<div class="row">操作人{{ tableData.detail.operatorSnap.name }}</div>
<div class="row">创建时间{{ tableData.detail.createdAt }}</div>
</div>
</el-dialog>
</template>
<script>
import dayjs from 'dayjs'
import { tbProductStockOperateDetail } from '@/api/invoicing'
export default {
data() {
return {
dayjs,
dialogVisible: false,
tableData: {
loading: false,
detail: {
stockSnap: [],
operatorSnap: {
name: ''
}
}
}
}
},
methods: {
show(id) {
this.dialogVisible = true
this.getTableData(id)
},
async getTableData(id) {
this.tableData.loading = true
try {
const res = await tbProductStockOperateDetail(id)
this.tableData.loading = false
this.tableData.detail = res
} catch (error) {
console.log(error);
}
}
}
}
</script>
<style scoped lang="scss">
.row {
padding-bottom: 10px;
}
</style>

View File

@@ -0,0 +1,151 @@
<template>
<div class="app-container">
<div class="head-container">
<el-row :gutter="20">
<el-col :span="6">
<el-input v-model="query.name" size="small" clearable placeholder="商品名称" style="width: 100%;"
class="filter-item" @keyup.enter.native="getTableData" />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-col>
</el-row>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="商品信息">
<template v-slot="scope">
<div class="shop_info">
<el-image :src="scope.row.coverImg" class="cover">
<div class="img_error" slot="error">
<i class="icon el-icon-document-delete"></i>
</div>
</el-image>
<div class="info">
<span>{{ scope.row.name }}</span>
<div>
<el-tag type="primary">{{ scope.row.typeEnum | typeEnum }}</el-tag>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="库存" prop="stockNumber">
<template v-slot="scope">
{{ `${scope.row.stockNumber} ${scope.row.unitName}` }}
</template>
</el-table-column>
<el-table-column label="库存开关">
<template v-slot="scope">
<el-switch v-model="scope.row.isStock" :active-value="1" :inactive-value="0"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template v-slot="scope">
<el-button type="text" size="mini"
@click="$refs.invoicingDetail.show(scope.row)">库存记录</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
<invoicingDetail ref="invoicingDetail" />
</div>
</template>
<script>
import { tbProductGet } from '@/api/invoicing'
import settings from '@/settings'
import invoicingDetail from './components/invoicingDetail'
export default {
components: {
invoicingDetail
},
data() {
return {
query: {
name: ''
},
tableData: {
page: 0,
size: 10,
total: 0,
sort: 'id',
loading: false,
list: []
}
}
},
filters: {
typeEnum(m) {
return settings.typeEnum.find(item => item.typeEnum == m).label
}
},
mounted() {
this.getTableData()
},
methods: {
async getTableData() {
this.tableData.loading = true
try {
const res = await tbProductGet({
page: this.tableData.page,
size: this.tableData.size,
sort: this.tableData.sort,
name: this.query.name,
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.list = res.content.map(item => {
let stockNumber = 0
for (let i of item.skuList) {
stockNumber += i.stockNumber
}
item.stockNumber = stockNumber
return item
})
this.tableData.total = res.totalElements
} catch (error) {
console.log(error);
}
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 重置查询
resetHandle() {
this.query.blurry = ''
this.tableData.page = 0;
this.getTableData()
}
}
}
</script>
<style scoped lang="scss">
.shop_info {
display: flex;
align-items: center;
.cover {
$size: 50px;
width: $size;
height: $size;
border-radius: 2px;
background-color: #efefef;
}
.info {
display: flex;
flex-direction: column;
padding-left: 10px;
justify-content: space-between;
}
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<div class="app-container">
<div class="head-container">
<div class="filter_wrap">
<el-date-picker v-model="query.createdAt" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss" @change="getTableData">
</el-date-picker>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="类型" prop="type">
<template v-slot="scope">
{{ scope.row.type == 'reject' ? '退货出库' : '供应商入库' }}
</template>
</el-table-column>
<el-table-column label="商品数量" prop="totalAmount">
<template v-slot="scope">
{{ scope.row.stockSnap.length }}
</template>
</el-table-column>
<el-table-column label="备注" prop="remark"></el-table-column>
<el-table-column label="操作人" prop="status">
<template v-slot="scope">
{{ scope.row.operatorSnap.account }}
</template>
</el-table-column>
<el-table-column label="创建时间" prop="updatedAt">
<template v-slot="scope">
{{ dayjs(scope.row.stockTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template v-slot="scope">
<el-button type="text" size="mini" @click="$refs.operatingDetail.show(scope.row.id)">
详情
</el-button>
<!-- <el-button type="text" size="mini" @click="$refs.operatingDetail.show(scope.row.id)">
作废
</el-button> -->
</template>
</el-table-column>
</el-table>
</div>
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
<operatingDetail ref="operatingDetail" />
</div>
</template>
<script>
import dayjs from 'dayjs'
import { tbProductStockOperateList } from '@/api/invoicing'
import operatingDetail from './components/operatingDetail'
export default {
components: {
operatingDetail
},
data() {
return {
dayjs,
query: {
createdAt: []
},
tableData: {
page: 0,
size: 10,
total: 0,
sort: 'id',
loading: false,
list: []
}
}
},
mounted() {
this.getTableData()
},
methods: {
async getTableData() {
this.tableData.loading = true
try {
const res = await tbProductStockOperateList({
page: this.tableData.page,
size: this.tableData.size,
shopId: localStorage.getItem('shopId'),
createdAt: this.query.createdAt
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error);
}
},
// 重置查询
resetHandle() {
this.query.createdAt = []
this.tableData.page = 0;
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
}
}
}
</script>

View File

@@ -0,0 +1,276 @@
<template>
<div class="app-container">
<div class="head-container">
<el-form ref="queryForm" :model="queryForm" :rules="queryRules" label-position="left" label-width="80px">
<el-form-item label="出库类型">
<div class="shop_type_box">
<div class="item" v-for="(item, index) in shopTypes" :key="index"
:class="{ active: shopTypesActive == index }" @click="changeTypeEnum(index)">
<div class="s_title">{{ item.label }}</div>
<div class="active_dot">
<i class="el-icon-check"></i>
</div>
</div>
</div>
</el-form-item>
<el-row>
<el-col :span="8" v-if="shopTypes[shopTypesActive].value == 'purveyor'">
<el-form-item label="供应商" prop="purveyorId">
<el-select v-model="queryForm.purveyorId" placeholder="请选择供应商" style="width: 220px;">
<el-option :label="item.purveyorName" :value="item.id" v-for="item in purveyorList"
:key="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="入库时间" prop="time">
<el-date-picker v-model="queryForm.time" type="date" format="yyyy-MM-dd"
value-format="yyyy-MM-dd" placeholder="选择日期" style="width: 220px;">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="shopTypes[shopTypesActive].value == 'purveyor'">
<el-col :span="8">
<el-form-item label="应收金额">
<el-input v-model="queryForm.totalAmount" placeholder="请输入应收金额"
style="width: 220px;"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="实收金额">
<el-input v-model="queryForm.paidAmount" placeholder="请输入实收金额"
style="width: 220px;"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="shopTypes[shopTypesActive].value == 'purveyor'">
<el-col :span="8">
<el-form-item label="付款时间">
<el-date-picker v-model="queryForm.paidAt" type="date" format="yyyy-MM-dd"
value-format="yyyy-MM-dd" placeholder="选择日期" style="width: 220px;">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="批号">
<el-input v-model="queryForm.batchNumber" placeholder="请输入批号"
style="width: 220px;"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="备注">
<el-input v-model="queryForm.remark" placeholder="请输入备注" style="width: 220px;"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="$refs.shopList.show(tableData.list)">选择商品</el-button>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<el-button type="primary" plain>
{{ tableData.list.length }}种商品金额合计<span style="color: red;">{{ queryForm.totalAmount }}</span>
</el-button>
</div>
<div class="head-container">
<el-table :data="tableData.list">
<el-table-column type="index" width="50"></el-table-column>
<el-table-column label="商品名称" prop="name">
<template v-slot="scope">
<div class="name_wrap">
<span class="name">{{ scope.row.name }}</span>
<el-tag type="info" v-if="scope.row.specSnap" size="mini">{{ scope.row.specSnap }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="进价">
<template v-slot="scope">
<el-input-number v-model="scope.row.guidePrice" :min="0" controls-position="right"
@change="e => scope.row.totalAmount = e * scope.row.number"></el-input-number>
<div class="tips">成本价{{ scope.row.costPrice }}/{{ scope.row.unitName }}</div>
</template>
</el-table-column>
<el-table-column label="数量">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" controls-position="right"
@change="e => scope.row.totalAmount = e * scope.row.guidePrice"></el-input-number>
<div class="tips">入库前{{ scope.row.stockNumber }}{{ scope.row.unitName }}</div>
</template>
</el-table-column>
<el-table-column label="小计">
<template v-slot="scope">
<el-input-number v-model="scope.row.totalAmount" :min="0"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="变动后剩余库存">
<template v-slot="scope">
{{ scope.row.stockNumber + scope.row.number }}{{ scope.row.unitName }}
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template v-slot="scope">
<el-button type="text" @click="tableData.list.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div>
<el-button type="primary" @click="submitHandle" :loading="queryFormLoading">确定</el-button>
</div>
<shopList ref="shopList" @success="selectShop" />
<el-dialog :visible.sync="showResult" :show-close="false" :close-on-press-escape="false"
:close-on-click-modal="false">
<el-result icon="success" title="入库提交成功" :subTitle="`共操作${tableData.list.length}件商品`">
<template slot="extra">
<el-button type="primary" size="medium" @click="resetHandle">创建新的入库单</el-button>
<router-link to="/invoicing/operating_record">
<el-button size="medium">历史提交</el-button>
</router-link>
</template>
</el-result>
</el-dialog>
</div>
</template>
<script>
import dayjs from 'dayjs'
import shopList from '@/components/shopList'
import { tbShopPurveyorGet, tbProductStockOperateOutAndOn } from '@/api/invoicing'
export default {
components: {
shopList
},
data() {
return {
shopTypesActive: 0,
shopTypes: [
{
label: '供应商入库',
value: 'purveyor'
},
{
label: '其他入库',
value: 'purchase'
}
],
resetForm: '',
queryFormLoading: false,
queryForm: {
batchNumber: '',
list: [],
paidAmount: 0,
paidAt: '',
purveyorId: '',
purveyorName: '',
remark: '',
time: dayjs().format('YYYY-MM-DD'),
totalAmount: 0,
type: 'purveyor',
shopId: localStorage.getItem('shopId')
},
queryRules: {
purveyorId: [
{
required: true,
message: ' ',
trigger: 'change'
}
],
time: [
{
required: true,
message: ' ',
trigger: 'change'
}
]
},
purveyorList: [],
tableData: {
list: []
},
showResult: false
}
},
mounted() {
this.resetForm = { ...this.queryForm }
this.tbShopPurveyorGet()
},
methods: {
// 提交
submitHandle() {
this.$refs.queryForm.validate(async valid => {
if (valid) {
try {
this.queryFormLoading = true
this.queryForm.list = this.tableData.list
await tbProductStockOperateOutAndOn(this.queryForm)
this.queryFormLoading = false
this.showResult = true
} catch (error) {
console.log(error)
this.queryFormLoading = false
}
}
})
},
// 选择商品
selectShop(res) {
let arr = []
res.forEach(item => {
item.skuList.forEach(i => {
arr.push({
name: item.name,
unitName: item.unitName,
productId: item.id,
number: 0,
totalAmount: '',
...i
})
})
})
console.log(arr)
this.tableData.list = arr
},
// 初始化
resetHandle() {
this.showResult = false
this.queryForm = { ...this.resetForm }
this.tableData.list = []
},
// 切换类型
changeTypeEnum(index) {
this.shopTypesActive = index
this.queryForm.type = this.shopTypes[index].value
},
// 获取供应商列表
async tbShopPurveyorGet() {
try {
const res = await tbShopPurveyorGet({
shopId: localStorage.getItem('shopId'),
page: 0,
size: 100
})
this.purveyorList = res.content
} catch (error) {
console.log(error)
}
}
}
}
</script>
<style scoped lang="scss">
.name_wrap {
display: flex;
align-items: center;
.name {
margin-right: 10px;
}
}
</style>

View File

@@ -0,0 +1,274 @@
<template>
<div class="app-container">
<div class="head-container">
<el-form ref="queryForm" :model="queryForm" :rules="queryRules" label-position="left" label-width="80px">
<el-form-item label="出库类型">
<div class="shop_type_box">
<div class="item" v-for="(item, index) in shopTypes" :key="index"
:class="{ active: shopTypesActive == index }" @click="changeTypeEnum(index)">
<div class="s_title">{{ item.label }}</div>
<div class="active_dot">
<i class="el-icon-check"></i>
</div>
</div>
</div>
</el-form-item>
<el-row>
<el-col :span="8" v-if="shopTypes[shopTypesActive].value == 'reject'">
<el-form-item label="供应商" prop="purveyorId">
<el-select v-model="queryForm.purveyorId" placeholder="请选择供应商" style="width: 220px;">
<el-option :label="item.purveyorName" :value="item.id" v-for="item in purveyorList"
:key="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="出库时间" prop="time">
<el-date-picker v-model="queryForm.time" type="date" format="yyyy-MM-dd"
value-format="yyyy-MM-dd" placeholder="选择日期" style="width: 220px;">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="shopTypes[shopTypesActive].value == 'reject'">
<el-col :span="8">
<el-form-item label="应收金额">
<el-input v-model="queryForm.totalAmount" placeholder="请输入应收金额"
style="width: 220px;"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="实收金额">
<el-input v-model="queryForm.paidAmount" placeholder="请输入实收金额" style="width: 220px;"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="shopTypes[shopTypesActive].value == 'reject'">
<el-col :span="8">
<el-form-item label="付款时间">
<el-date-picker v-model="queryForm.paidAt" type="date" format="yyyy-MM-dd"
value-format="yyyy-MM-dd" placeholder="选择日期" style="width: 220px;">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="批号">
<el-input v-model="queryForm.batchNumber" placeholder="请输入批号" style="width: 220px;"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="备注">
<el-input v-model="queryForm.remark" placeholder="请输入备注" style="width: 220px;"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="$refs.shopList.show(tableData.list)">选择商品</el-button>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<el-button type="primary" plain>
{{ tableData.list.length }}种商品金额合计<span style="color: red;">{{ queryForm.totalAmount }}</span>
</el-button>
</div>
<div class="head-container">
<el-table :data="tableData.list">
<el-table-column type="index" width="50"></el-table-column>
<el-table-column label="商品名称" prop="name">
<template v-slot="scope">
<div class="name_wrap">
<span class="name">{{ scope.row.name }}</span>
<el-tag type="info" v-if="scope.row.specSnap" size="mini">{{ scope.row.specSnap }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="进价">
<template v-slot="scope">
<el-input-number v-model="scope.row.guidePrice" :min="0" controls-position="right"
@change="e => scope.row.totalAmount = e * scope.row.number"></el-input-number>
<div class="tips">成本价{{ scope.row.costPrice }}/{{ scope.row.unitName }}</div>
</template>
</el-table-column>
<el-table-column label="数量">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" controls-position="right"
@change="e => scope.row.totalAmount = e * scope.row.guidePrice"></el-input-number>
<div class="tips">出库前{{ scope.row.stockNumber }}{{ scope.row.unitName }}</div>
</template>
</el-table-column>
<el-table-column label="小计">
<template v-slot="scope">
<el-input-number v-model="scope.row.totalAmount" :min="0"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="变动后剩余库存">
<template v-slot="scope">
{{ scope.row.stockNumber - scope.row.number }}{{ scope.row.unitName }}
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template v-slot="scope">
<el-button type="text" @click="tableData.list.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div>
<el-button type="primary" @click="submitHandle" :loading="queryFormLoading">确定</el-button>
</div>
<shopList ref="shopList" @success="selectShop" />
<el-dialog :visible.sync="showResult" :show-close="false" :close-on-press-escape="false"
:close-on-click-modal="false">
<el-result icon="success" title="出库提交成功" :subTitle="`共操作${tableData.list.length}件商品`">
<template slot="extra">
<el-button type="primary" size="medium" @click="resetHandle">创建新的出库单</el-button>
<router-link to="/invoicing/operating_record">
<el-button size="medium">历史提交</el-button>
</router-link>
</template>
</el-result>
</el-dialog>
</div>
</template>
<script>
import dayjs from 'dayjs'
import shopList from '@/components/shopList'
import { tbShopPurveyorGet, tbProductStockOperateOutAndOn } from '@/api/invoicing'
export default {
components: {
shopList
},
data() {
return {
shopTypesActive: 0,
shopTypes: [
{
label: '供应商退货',
value: 'reject'
},
{
label: '其他出库',
value: 'other-out'
}
],
resetForm: '',
queryFormLoading: false,
queryForm: {
batchNumber: '',
list: [],
paidAmount: 0,
paidAt: '',
purveyorId: '',
purveyorName: '',
remark: '',
time: dayjs().format('YYYY-MM-DD'),
totalAmount: 0,
type: 'reject',
shopId: localStorage.getItem('shopId')
},
queryRules: {
purveyorId: [
{
required: true,
message: ' ',
trigger: 'change'
}
],
time: [
{
required: true,
message: ' ',
trigger: 'change'
}
]
},
purveyorList: [],
tableData: {
list: []
},
showResult: false
}
},
mounted() {
this.resetForm = { ...this.queryForm }
this.tbShopPurveyorGet()
},
methods: {
// 提交
submitHandle() {
this.$refs.queryForm.validate(async valid => {
if (valid) {
try {
this.queryFormLoading = true
this.queryForm.list = this.tableData.list
await tbProductStockOperateOutAndOn(this.queryForm)
this.queryFormLoading = false
this.showResult = true
} catch (error) {
console.log(error)
this.queryFormLoading = false
}
}
})
},
// 选择商品
selectShop(res) {
let arr = []
res.forEach(item => {
item.skuList.forEach(i => {
arr.push({
name: item.name,
unitName: item.unitName,
productId: item.id,
number: 0,
totalAmount: '',
...i
})
})
})
console.log(arr)
this.tableData.list = arr
},
// 初始化
resetHandle() {
this.showResult = false
this.queryForm = { ...this.resetForm }
this.tableData.list = []
},
// 切换类型
changeTypeEnum(index) {
this.shopTypesActive = index
this.queryForm.type = this.shopTypes[index].value
},
// 获取供应商列表
async tbShopPurveyorGet() {
try {
const res = await tbShopPurveyorGet({
shopId: localStorage.getItem('shopId'),
page: 0,
size: 100
})
this.purveyorList = res.content
} catch (error) {
console.log(error)
}
}
}
}
</script>
<style scoped lang="scss">
.name_wrap {
display: flex;
align-items: center;
.name {
margin-right: 10px;
}
}
</style>

View File

@@ -0,0 +1,136 @@
<template>
<div class="app-container">
<div class="head-container">
<div class="filter_wrap">
<el-input v-model="query.name" clearable placeholder="供应商" @keyup.enter.native="getTableData"
style="width: 200px;" />
<el-select v-model="query.type" placeholder="付款状态">
<el-option :label="item.label" :value="item.value" v-for="item in types" :key="item.id"></el-option>
</el-select>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="供应商" prop="purveyorName"></el-table-column>
<el-table-column label="剩余付款金额" prop="waitAmount">
<template v-slot="scope">
<span class="num" v-if="scope.row.waitAmount > 0">{{ scope.row.waitAmount }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="待付款笔数" prop="waitCount">
<template v-slot="scope">
<template v-if="scope.row.waitCount > 0">
<span class="count">{{ scope.row.waitCount }}</span>未付
</template>
<template v-else>-</template>
</template>
</el-table-column>
<el-table-column label="状态" prop="type">
<template v-slot="scope">
<el-tag :type="scope.row.type == 0 ? 'warning' : 'success'">
{{ scope.row.type == 0 ? '待支付' : '已完结' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="上笔进货日期" prop="lastTransactAt">
<template v-slot="scope">
{{ dayjs(scope.row.lastTransactAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template v-slot="scope">
<router-link :to="{ name: 'purchase_detail', query: { purveyorId: scope.row.purveyorId } }">
<el-button type="text" size="mini">查看详情</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</template>
<script>
import dayjs from 'dayjs'
import { tbShopPurveyorTransactGet, dictDetail } from '@/api/invoicing'
export default {
data() {
return {
dayjs,
types: [],
query: {
name: '',
type: ''
},
tableData: {
page: 0,
size: 10,
total: 0,
sort: 'id',
loading: false,
list: []
}
}
},
mounted() {
this.dictDetail()
this.getTableData()
},
methods: {
async getTableData() {
this.tableData.loading = true
try {
const res = await tbShopPurveyorTransactGet({
page: this.tableData.page,
size: this.tableData.size,
sort: this.tableData.sort,
purveyorName: this.query.name,
status: this.query.type,
type: 'purveyor',
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error);
}
},
// 重置查询
resetHandle() {
this.query.name = ''
this.query.type = ''
this.tableData.page = 0;
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
async dictDetail() {
const { content } = await dictDetail({
dictName: 'purveyor_transact_status',
size: 100,
page: 0
})
this.types = content
}
}
}
</script>
<style scoped lang="scss">
.num {
color: #F56C6C;
font-weight: bold;
}
.count {
color: #409EFF;
}
</style>

View File

@@ -0,0 +1,350 @@
<template>
<div class="app-container">
<div class="head-container">
<el-radio-group v-model="query.time" @change="timeChange">
<el-radio-button :label="item.value" v-for="item in timeList" :key="item.label">{{ item.label
}}</el-radio-button>
</el-radio-group>
</div>
<div class="head-container" v-if="query.time == 'custom'">
<el-date-picker v-model="query.createdAt" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss" @change="getTableData">
</el-date-picker>
</div>
<div class="head-container">
<div class="filter_wrap">
<el-select v-model="query.type" placeholder="付款状态">
<el-option :label="item.label" :value="item.value" v-for="item in types" :key="item.id"></el-option>
</el-select>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
</div>
<div class="head-container">
<div class="info_list">
<div class="item">
<div class="icon_wrap" style="background-color: #F2D7FF;">
<i class="icon el-icon-info" style="color:#C978EE;"></i>
</div>
<div class="info">
<div class="n">{{ info.totalAmount }}</div>
<div class="intro">总交易{{ info.waitCount }}</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="background-color: #DFFECC;">
<i class="icon el-icon-success" style="color:#47B505;"></i>
</div>
<div class="info">
<div class="n">{{ info.paidAmount }}</div>
<div class="intro">已支付金额</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="background-color: #FFE7D6;">
<i class="icon el-icon-circle-plus" style="color: #FF6B00;"></i>
</div>
<div class="info">
<div class="n">{{ info.waitAmount }}</div>
<div class="intro">待支付金额</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="background-color: #FFF1D5;">
<i class="icon el-icon-warning" style="color: #FEB420;"></i>
</div>
<div class="info">
<div class="n">{{ info.waitNumber }}</div>
<div class="intro">待支付笔数</div>
</div>
</div>
</div>
</div>
<div class="head-container">
<div class="select_count_wrap">
<div class="select_count">
<i class="icon el-icon-warning"></i>
已选中
<span class="n">{{ selectCount }}</span>
</div>
<el-button>批量付款</el-button>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading" @select="selectHandle"
@select-all="selectHandle">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column label="创建日期" prop="createdAt">
<template v-slot="scope">
{{ dayjs(scope.row.createdAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="类型" prop="type">
<template v-slot="scope">
进货单
</template>
</el-table-column>
<el-table-column label="总金额" prop="totalAmount">
<template v-slot="scope">
{{ scope.row.totalAmount }}
</template>
</el-table-column>
<el-table-column label="待付款金额" prop="waitAmount">
<template v-slot="scope">
<span class="num" v-if="scope.row.waitAmount > 0">{{ scope.row.waitAmount }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="状态" prop="status">
<template v-slot="scope">
<el-tag type="info">{{ types.find(item => item.value == scope.row.status).label }}</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" prop="remark"></el-table-column>
<el-table-column label="付款时间" prop="updatedAt">
<template v-slot="scope">
{{ scope.row.paidAt && dayjs(scope.row.paidAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template v-slot="scope">
<router-link :to="{ name: 'purchase_detail', query: { purveyorId: scope.row.purveyorId } }">
<el-button type="text" size="mini">查看详情</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</template>
<script>
import dayjs from 'dayjs'
import { tbShopPurveyorTransactInfo, dictDetail, tbShopPurveyorTransactSum } from '@/api/invoicing'
export default {
data() {
return {
dayjs,
timeList: [
{
label: '全部',
value: 'all'
},
{
label: '今天',
value: '0'
},
{
label: '昨天',
value: '-1'
},
{
label: '最近7天',
value: '-7'
},
{
label: '最近30天',
value: '-30'
},
{
label: '本周',
value: 'week'
},
{
label: '本月',
value: 'moth'
},
{
label: '自定义',
value: 'custom'
}
],
types: [],
selectCount: 0,
query: {
type: '',
time: 'all',
createdAt: []
},
tableData: {
page: 0,
size: 10,
total: 0,
sort: 'id',
loading: false,
list: []
},
info: ''
}
},
mounted() {
this.dictDetail()
this.getTableData()
this.tbShopPurveyorTransactSum()
},
methods: {
async getTableData() {
this.tableData.loading = true
try {
const res = await tbShopPurveyorTransactInfo({
page: this.tableData.page,
size: this.tableData.size,
sort: this.tableData.sort,
purveyorId: this.$route.query.purveyorId,
status: this.query.type,
type: 'purveyor',
createdAt: this.query.createdAt
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error);
}
},
// 重置查询
resetHandle() {
this.query.time = 'all'
this.query.createdAt = []
this.tableData.page = 0;
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
async dictDetail() {
const { content } = await dictDetail({
dictName: 'purveyor_transact_status',
size: 100,
page: 0
})
this.types = content
},
// 选择时间
timeChange(e) {
this.query.createdAt = []
const format0 = 'YYYY-MM-DD 00:00:00'
const format1 = 'YYYY-MM-DD 23:59:59'
switch (e) {
case '0':
this.query.createdAt = [dayjs().format(format0), dayjs().format(format1)]
break;
case '-1':
this.query.createdAt = [dayjs().add(-1, 'day').format(format0), dayjs().add(-1, 'day').format(format1)]
break;
case '-7':
this.query.createdAt = [dayjs().add(-7, 'day').format(format0), dayjs().format(format1)]
break;
case '-30':
this.query.createdAt = [dayjs().add(-30, 'day').format(format0), dayjs().format(format1)]
break;
case 'week':
this.query.createdAt = [dayjs().startOf('week').format(format0), dayjs().endOf('week').format(format1)]
break;
case 'moth':
this.query.createdAt = [dayjs().startOf('month').format(format0), dayjs().endOf('month').format(format1)]
break;
default:
break;
}
this.getTableData()
},
selectHandle(selection, row) {
this.selectCount = 0
this.selectCount = selection.length
},
async tbShopPurveyorTransactSum() {
this.info = await tbShopPurveyorTransactSum({ purveyorId: this.$route.query.purveyorId, type: 'purveyor' })
}
}
}
</script>
<style scoped lang="scss">
.select_count_wrap {
display: flex;
align-items: center;
.select_count {
display: flex;
align-items: center;
justify-content: center;
background-color: #e7f3ff;
width: 120px;
height: 40px;
font-size: 14px;
margin-right: 10px;
border-radius: 4px;
.icon {
margin-right: 4px;
}
.n {
margin: 0 4px;
}
.icon,
.n {
color: #409EFF;
}
}
}
.num {
color: #F56C6C;
font-weight: bold;
}
.count {
color: #409EFF;
}
.info_list {
display: flex;
gap: 20px;
.item {
flex: 1;
background-color: #fff;
padding: 20px;
display: flex;
align-items: center;
background-color: #f5f5f5;
.icon_wrap {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
padding: 2px;
.icon {
font-size: 40px;
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 20px;
.n {
font-size: 28px;
text-indent: -6px;
}
.intro {
font-size: 12px;
color: #999;
}
}
}
}
</style>

View File

@@ -0,0 +1,136 @@
<template>
<div class="app-container">
<div class="head-container">
<div class="filter_wrap">
<el-input v-model="query.name" clearable placeholder="供应商" @keyup.enter.native="getTableData"
style="width: 200px;" />
<el-select v-model="query.type" placeholder="付款状态">
<el-option :label="item.label" :value="item.value" v-for="item in types" :key="item.id"></el-option>
</el-select>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="供应商" prop="purveyorName"></el-table-column>
<el-table-column label="剩余付款金额" prop="waitAmount">
<template v-slot="scope">
<span class="num" v-if="scope.row.waitAmount > 0">{{ scope.row.waitAmount }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="待付款笔数" prop="waitCount">
<template v-slot="scope">
<template v-if="scope.row.waitCount > 0">
<span class="count">{{ scope.row.waitCount }}</span>未付
</template>
<template v-else>-</template>
</template>
</el-table-column>
<el-table-column label="状态" prop="type">
<template v-slot="scope">
<el-tag :type="scope.row.type == 0 ? 'warning' : 'success'">
{{ scope.row.type == 0 ? '待支付' : '已完结' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="上笔进货日期" prop="lastTransactAt">
<template v-slot="scope">
{{ dayjs(scope.row.lastTransactAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template v-slot="scope">
<router-link :to="{ name: 'purchase_detail', query: { purveyorId: scope.row.purveyorId } }">
<el-button type="text" size="mini">查看详情</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</template>
<script>
import dayjs from 'dayjs'
import { tbShopPurveyorTransactGet, dictDetail } from '@/api/invoicing'
export default {
data() {
return {
dayjs,
types: [],
query: {
name: '',
type: ''
},
tableData: {
page: 0,
size: 10,
total: 0,
sort: 'id',
loading: false,
list: []
}
}
},
mounted() {
this.dictDetail()
this.getTableData()
},
methods: {
async getTableData() {
this.tableData.loading = true
try {
const res = await tbShopPurveyorTransactGet({
page: this.tableData.page,
size: this.tableData.size,
sort: this.tableData.sort,
purveyorName: this.query.name,
status: this.query.type,
type: 'reject',
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error);
}
},
// 重置查询
resetHandle() {
this.query.name = ''
this.query.type = ''
this.tableData.page = 0;
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
async dictDetail() {
const { content } = await dictDetail({
dictName: 'purveyor_transact_status',
size: 100,
page: 0
})
this.types = content
}
}
}
</script>
<style scoped lang="scss">
.num {
color: #F56C6C;
font-weight: bold;
}
.count {
color: #409EFF;
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<div class="app-container">
<div class="head-container">
<div class="filter_wrap">
<el-input v-model="query.name" size="small" clearable placeholder="供应商" @keyup.enter.native="getTableData"
style="width: 200px;" />
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
</div>
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addSupplier.show()">添加供应商</el-button>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="供应商" prop="purveyorName"></el-table-column>
<el-table-column label="联系电话" prop="purveyorTelephone"></el-table-column>
<el-table-column label="地址" prop="address"></el-table-column>
<el-table-column label="标签" prop="tip"></el-table-column>
<el-table-column label="备注" prop="remark"></el-table-column>
<el-table-column label="操作" width="200">
<template v-slot="scope">
<el-button type="text" size="mini" round icon="el-icon-edit"
@click="$refs.addSupplier.show(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="delHandle([scope.row.id])">
<el-button type="text" size="mini" round icon="el-icon-delete" slot="reference">
删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
<addSupplier ref="addSupplier" @success="getTableData" />
</div>
</template>
<script>
import { tbShopPurveyorGet, tbShopPurveyor } from '@/api/invoicing'
import addSupplier from '../components/addSupplier'
export default {
components: {
addSupplier
},
data() {
return {
query: {
name: ''
},
tableData: {
page: 0,
size: 10,
total: 0,
sort: 'id',
loading: false,
list: []
}
}
},
mounted() {
this.getTableData()
},
methods: {
// 删除
async delHandle(ids) {
try {
await tbShopPurveyor(ids, 'delete')
this.$notify({
title: '成功',
message: `删除成功`,
type: 'success'
});
this.getTableData()
} catch (error) {
console.log(error)
}
},
async getTableData() {
this.tableData.loading = true
try {
const res = await tbShopPurveyorGet({
page: this.tableData.page,
size: this.tableData.size,
sort: this.tableData.sort,
purveyorName: this.query.name,
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error);
}
},
// 重置查询
resetHandle() {
this.query.name = ''
this.tableData.page = 0;
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
}
}
}
</script>

234
src/views/login.vue Normal file
View File

@@ -0,0 +1,234 @@
<template>
<div class="login" :style="'background-image:url(' + Background + ');'">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-position="left" label-width="0px"
class="login-form">
<h3 class="title">
银收客后台管理
</h3>
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号">
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码"
@keyup.enter.native="handleLogin">
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
</el-input>
</el-form-item>
<el-form-item prop="code">
<div class="code_wrap">
<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">
</div>
</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">
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
</el-form-item>
</el-form>
<!-- 底部 -->
<div v-if="$store.state.settings.showFooter" id="el-login-footer">
<span v-html="$store.state.settings.footerTxt" />
<span v-if="$store.state.settings.caseNumber"> </span>
<a href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank">{{ $store.state.settings.caseNumber }}</a>
</div>
</div>
</template>
<script>
import { encrypt } from '@/utils/rsaEncrypt'
import Config from '@/settings'
import { getCodeImg } from '@/api/login'
import Cookies from 'js-cookie'
import qs from 'qs'
import Background from '@/assets/images/background_img.jpg'
export default {
name: 'Login',
data() {
return {
Background: Background,
codeUrl: '',
cookiePass: '',
loginForm: {
username: '',
password: '',
rememberMe: false,
code: '',
uuid: ''
},
loginRules: {
username: [{ required: true, trigger: 'blur', message: '用户名不能为空' }],
password: [{ required: true, trigger: 'blur', message: '密码不能为空' }],
code: [{ required: true, trigger: 'change', message: '验证码不能为空' }]
},
loading: false,
redirect: undefined
}
},
watch: {
$route: {
handler: function (route) {
const data = route.query
if (data && data.redirect) {
this.redirect = data.redirect
delete data.redirect
if (JSON.stringify(data) !== '{}') {
this.redirect = this.redirect + '&' + qs.stringify(data, { indices: false })
}
}
},
immediate: true
}
},
created() {
// 获取验证码
this.getCode()
// 获取用户名密码等Cookie
this.getCookie()
// token 过期提示
this.point()
},
methods: {
getCode() {
getCodeImg().then(res => {
console.log(res);
this.codeUrl = res.img
this.loginForm.uuid = res.uuid
})
},
getCookie() {
const username = Cookies.get('username')
let password = Cookies.get('password')
const rememberMe = Cookies.get('rememberMe')
// 保存cookie里面的加密后的密码
this.cookiePass = password === undefined ? '' : password
password = password === undefined ? this.loginForm.password : password
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password: password,
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
code: ''
}
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
const user = {
username: this.loginForm.username,
password: this.loginForm.password,
rememberMe: this.loginForm.rememberMe,
code: this.loginForm.code,
uuid: this.loginForm.uuid
}
if (user.password !== this.cookiePass) {
user.password = encrypt(user.password)
}
if (valid) {
this.loading = true
if (user.rememberMe) {
Cookies.set('username', user.username, { expires: Config.passCookieExpires })
Cookies.set('password', user.password, { expires: Config.passCookieExpires })
Cookies.set('rememberMe', user.rememberMe, { expires: Config.passCookieExpires })
} else {
Cookies.remove('username')
Cookies.remove('password')
Cookies.remove('rememberMe')
}
this.$store.dispatch('Login', user).then(() => {
this.loading = false
this.$router.push({ path: this.redirect || '/' })
}).catch(() => {
this.loading = false
this.getCode()
})
} else {
console.log('error submit!!')
return false
}
})
},
point() {
const point = Cookies.get('point') !== undefined
if (point) {
this.$notify({
title: '提示',
message: '当前登录状态已过期,请重新登录!',
type: 'warning',
duration: 5000
})
Cookies.remove('point')
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
display: flex;
justify-content: flex-end;
align-items: center;
height: 100%;
background-size: cover;
padding-right: 15%;
}
.title {
margin: 0 auto 50px auto;
text-align: center;
color: #707070;
font-size: 36px;
}
.login-form {
border-radius: 6px;
background: #ffffff;
width: 500px;
padding: 50px 50px 50px 50px;
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.code_wrap {
display: flex;
justify-content: space-between;
}
.login-code {
width: 33%;
display: inline-block;
height: 38px;
img {
cursor: pointer;
vertical-align: middle
}
}
</style>

144
src/views/mnt/app/index.vue Normal file
View File

@@ -0,0 +1,144 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.name" clearable placeholder="输入名称搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<el-button
slot="left"
v-permission="['admin','app:add']"
:disabled="!currentRow"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-plus"
@click="copy"
>复制</el-button>
</crudOperation>
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="800px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="应用名称" prop="name">
<el-input v-model="form.name" style="width: 670px" placeholder="部署后的文件或者目录名称用于备份" />
</el-form-item>
<el-form-item label="应用端口" prop="port">
<el-input-number v-model.number="form.port" placeholder="例如8080" />
</el-form-item>
<el-form-item label="上传目录" prop="uploadPath">
<el-input v-model="form.uploadPath" style="width: 670px" placeholder="例如: /opt/upload" />
</el-form-item>
<el-form-item label="部署目录" prop="deployPath">
<el-input v-model="form.deployPath" style="width: 670px" placeholder="例如: /opt/app" />
</el-form-item>
<el-form-item label="备份目录" prop="backupPath">
<el-input v-model="form.backupPath" style="width: 670px" placeholder="例如: /opt/backup" />
</el-form-item>
<el-form-item label="部署脚本" prop="deployScript">
<el-input v-model="form.deployScript" :rows="3" type="textarea" autosize style="width: 670px" placeholder="" />
</el-form-item>
<el-form-item label="启动脚本" prop="startScript">
<el-input v-model="form.startScript" :rows="3" type="textarea" autosize style="width: 670px" placeholder="" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="应用名称" />
<el-table-column prop="port" label="端口号" />
<el-table-column prop="uploadPath" label="上传目录" />
<el-table-column prop="deployPath" label="部署目录" />
<el-table-column prop="backupPath" label="备份目录" />
<el-table-column prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','app:edit','app:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudApp from '@/api/mnt/app'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, name: null, port: 8080, uploadPath: '/opt/upload', deployPath: '/opt/app', backupPath: '/opt/backup', startScript: null, deployScript: null }
export default {
name: 'App',
components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '应用', url: 'api/app', crudMethod: { ...crudApp }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
currentRow: null,
permission: {
add: ['admin', 'app:add'],
edit: ['admin', 'app:edit'],
del: ['admin', 'app:del']
},
rules: {
name: [
{ required: true, message: '请输入应用名称', trigger: 'blur' }
],
port: [
{ required: true, message: '请输入应用端口', trigger: 'blur', type: 'number' }
],
uploadPath: [
{ required: true, message: '请输入上传目录', trigger: 'blur' }
],
deployPath: [
{ required: true, message: '请输入部署目录', trigger: 'blur' }
],
backupPath: [
{ required: true, message: '请输入备份目录', trigger: 'blur' }
],
startScript: [
{ required: true, message: '请输入启动脚本', trigger: 'blur' }
],
deployScript: [
{ required: true, message: '请输入部署脚本', trigger: 'blur' }
]
}
}
},
methods: {
copy() {
for (const key in this.currentRow) {
this.form[key] = this.currentRow[key]
}
this.form.id = null
this.form.createTime = null
this.crud.toAdd()
},
handleCurrentChange(row) {
this.currentRow = JSON.parse(JSON.stringify(row))
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,86 @@
<template>
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="执行脚本" width="400px">
<el-form ref="form" :rules="rules" size="small">
<el-upload
:action="databaseUploadApi"
:data="databaseInfo"
:headers="headers"
:on-success="handleSuccess"
:on-error="handleError"
class="upload-demo"
drag
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div slot="tip" class="el-upload__tip">上传后系统会自动执行SQL脚本</div>
</el-upload>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="cancel">关闭</el-button>
</div>
</el-dialog>
</template>
<script>
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
export default {
props: {
databaseInfo: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
loading: false,
dialog: false,
headers: {
Authorization: getToken()
},
rules: {}
}
},
computed: {
...mapGetters(['databaseUploadApi'])
},
mounted() {
},
methods: {
cancel() {
this.dialog = false
},
handleSuccess(response, file, fileList) {
if (response === 'success') {
this.$notify({
title: '执行成功',
type: 'success',
duration: 2500
})
} else {
this.$notify({
title: response,
type: 'error',
duration: 0
})
}
},
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.$notify({
title: msg.message,
type: 'error',
duration: 0
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,148 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable placeholder="模糊搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<el-button
slot="right"
v-permission="['admin','database:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="warning"
icon="el-icon-upload"
@click="execute"
>执行脚本
</el-button>
</crudOperation>
</div>
<!--表单组件-->
<eForm ref="execute" :database-info="currentRow" />
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="530px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="连接名称" prop="name">
<el-input v-model="form.name" style="width: 370px" />
</el-form-item>
<el-form-item label="JDBC地址" prop="jdbcUrl">
<el-input v-model="form.jdbcUrl" style="width: 300px" />
<el-button :loading="loading" type="success" @click="testConnectDatabase">测试</el-button>
</el-form-item>
<el-form-item label="用户" prop="userName">
<el-input v-model="form.userName" style="width: 370px" />
</el-form-item>
<el-form-item label="密码" prop="pwd">
<el-input v-model="form.pwd" type="password" style="width: 370px" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row stripe style="width: 100%" @selection-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" width="130px" label="数据库名称" />
<el-table-column prop="jdbcUrl" label="连接地址" />
<el-table-column prop="userName" width="200px" label="用户名" />
<el-table-column prop="createTime" width="200px" label="创建日期" />
<el-table-column v-if="checkPer(['admin','database:edit','database:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudDatabase from '@/api/mnt/database'
import { testDbConnect } from '@/api/mnt/connect'
import eForm from './execute'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, name: null, jdbcUrl: 'jdbc:mysql://', userName: null, pwd: null }
export default {
name: 'DataBase',
components: { eForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '数据库', url: 'api/database', crudMethod: { ...crudDatabase }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
currentRow: {},
selectIndex: '',
databaseInfo: '',
loading: false,
permission: {
add: ['admin', 'database:add'],
edit: ['admin', 'database:edit'],
del: ['admin', 'database:del']
},
rules: {
name: [
{ required: true, message: '请输入数据库名称', trigger: 'blur' }
],
jdbcUrl: [
{ required: true, message: '请输入数据库连接地址', trigger: 'blur' }
],
userName: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
pwd: [
{ required: true, message: '请输入数据库密码', trigger: 'blur' }
]
}
}
},
methods: {
testConnectDatabase() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
testDbConnect(this.form).then((res) => {
this.loading = false
this.crud.notify(res ? '连接成功' : '连接失败', res ? 'success' : 'error')
}).catch(() => {
this.loading = false
})
}
})
},
execute() {
this.$refs.execute.dialog = true
},
handleCurrentChange(selection) {
this.crud.selections = selection
if (selection.length === 1) {
const row = selection[0]
this.selectIndex = row.id
this.currentRow = row
} else {
this.currentRow = {}
this.selectIndex = ''
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,190 @@
<template>
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="应用部署" width="400px">
<el-form ref="form" :model="form" :rules="rules" size="small">
<el-upload
:action="deployUploadApi"
:data="deployInfo"
:headers="headers"
:on-success="handleSuccess"
:on-error="handleError"
class="upload-demo"
drag
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div slot="tip" class="el-upload__tip">多个应用上传文件名称为all.zip,数据库更新脚本扩展名为.sql,上传成功后系统自动部署系统</div>
</el-upload>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="cancel">关闭</el-button>
</div>
</el-dialog>
</template>
<script>
import { add, edit, getApps, getServers } from '@/api/mnt/deploy'
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
export default {
props: {},
data() {
return {
loading: false,
dialog: false,
apps: [],
servers: [],
headers: {
Authorization: getToken()
},
deployInfo: {},
form: {
id: '',
appId: '',
ip: '',
selectIp: []
},
rules: {}
}
},
computed: {
...mapGetters(['deployUploadApi'])
},
created() {
this.initWebSocket()
},
mounted() {
this.initSelect()
},
methods: {
cancel() {
this.resetForm()
},
doSubmit() {
this.loading = true
if (this.isAdd) {
this.doAdd()
} else {
this.doEdit()
}
},
joinIp() {
this.form.ip = ''
this.form.selectIp.forEach(ip => {
if (this.form.ip !== '') {
this.form.ip += ','
}
this.form.ip += ip
})
},
doAdd() {
this.joinIp()
add(this.form)
.then(res => {
this.resetForm()
this.$notify({
title: '添加成功',
type: 'success',
duration: 2500
})
this.loading = false
this.$parent.init()
})
.catch(err => {
this.loading = false
console.log(err.response.data.message)
})
},
doEdit() {
this.joinIp()
edit(this.form)
.then(res => {
this.resetForm()
this.$notify({
title: '修改成功',
type: 'success',
duration: 2500
})
this.loading = false
this.$parent.init()
})
.catch(err => {
this.loading = false
console.log(err.response.data.message)
})
},
resetForm() {
this.dialog = false
this.$refs['form'].resetFields()
this.form = {
id: '',
appId: '',
ip: '',
selectIp: []
}
},
initSelect() {
getApps().then(res => {
this.apps = res.content
})
getServers().then(res => {
this.servers = res.content
})
},
handleSuccess(response, file, fileList) {
this.cancel()
},
// 监听上传失败
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.$notify({
title: msg.message,
type: 'error',
duration: 2500
})
},
initWebSocket() {
const wsUri = (process.env.VUE_APP_WS_API === '/' ? '/' : (process.env.VUE_APP_WS_API + '/')) + 'webSocket/deploy'
this.websock = new WebSocket(wsUri)
this.websock.onerror = this.webSocketOnError
this.websock.onmessage = this.webSocketOnMessage
},
webSocketOnError(e) {
this.$notify({
title: 'WebSocket连接发生错误',
type: 'error',
duration: 0
})
},
webSocketOnMessage(e) {
const data = JSON.parse(e.data)
if (data.msgType === 'INFO') {
this.$notify({
title: '',
message: data.msg,
type: 'success',
dangerouslyUseHTMLString: true,
duration: 5500
})
} else if (data.msgType === 'ERROR') {
this.$notify({
title: '',
message: data.msg,
dangerouslyUseHTMLString: true,
type: 'error',
duration: 0
})
}
},
webSocketSend(agentData) {
this.websock.send(agentData)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,229 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.appName" clearable placeholder="输入应用名称查询" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<template slot="right">
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-upload"
@click="sysRestore"
>系统还原
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-upload"
@click="serverStatus"
>状态查询
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="success"
icon="el-icon-upload"
@click="startServer"
>启动
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="danger"
icon="el-icon-upload"
@click="stopServer"
>停止
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="warning"
icon="el-icon-upload"
@click="deploy"
>一键部署
</el-button>
</template>
</crudOperation>
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="应用" prop="app.id">
<el-select v-model.number="form.app.id" placeholder="请选择" style="width: 370px">
<el-option v-for="item in apps" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="服务器" prop="deploys">
<el-select v-model="form.deploys" multiple placeholder="请选择" style="width: 370px">
<el-option v-for="item in servers" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--统还原组件-->
<fForm ref="sysRestore" :key="times" :app-name="appName" />
<dForm ref="deploy" />
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row stripe style="width: 100%" @selection-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="app.name" label="应用名称" />
<el-table-column prop="servers" label="服务器列表" />
<el-table-column prop="createTime" label="部署日期" />
<el-table-column v-if="checkPer(['admin','deploy:edit','deploy:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudDeploy from '@/api/mnt/deploy'
import dForm from './deploy'
import fForm from './sysRestore'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, app: { id: null }, deploys: [] }
export default {
name: 'Deploy',
components: { dForm, fForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '部署', url: 'api/deploy', crudMethod: { ...crudDeploy }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
currentRow: {}, selectIndex: '', appName: '', urlHistory: '',
times: 0, appId: '', deployId: '', apps: [], servers: [],
permission: {
add: ['admin', 'deploy:add'],
edit: ['admin', 'deploy:edit'],
del: ['admin', 'deploy:del']
},
rules: {
'app.id': [
{ required: true, message: '应用不能为空', trigger: 'blur', type: 'number' }
],
deploys: [
{ required: true, message: '服务器不能为空', trigger: 'blur' }
]
}
}
},
methods: {
[CRUD.HOOK.beforeRefresh]() {
this.selectIndex = ''
return true
},
// 新增编辑前做的操作
[CRUD.HOOK.beforeToCU](crud, form) {
this.initSelect()
const deploys = []
form.deploys.forEach(function(deploy, index) {
deploys.push(deploy.id)
})
this.form.deploys = deploys
},
// 提交前
[CRUD.HOOK.beforeSubmit]() {
const deploys = []
this.form.deploys.forEach(function(data, index) {
const deploy = { id: data }
deploys.push(deploy)
})
this.form.deploys = deploys
return true
},
deploy() {
this.$refs.deploy.dialog = true
this.$refs.deploy.deployInfo = this.currentRow
},
sysRestore() {
this.$refs.sysRestore.dialog = true
},
handleCurrentChange(selection) {
this.crud.selections = selection
if (selection.length === 1) {
const row = selection[0]
this.selectIndex = row.id
this.currentRow = row
this.appName = row.app.name
this.times = this.times + 1
this.appId = row.appId
this.deployId = row.id
} else {
this.currentRow = {}
this.selectIndex = ''
}
},
startServer() {
crudDeploy.startServer(JSON.stringify(this.currentRow))
.then(res => {
})
.catch(err => {
console.log('error:' + err.response.data.message)
})
},
stopServer() {
crudDeploy.stopServer(JSON.stringify(this.currentRow))
.then(res => {
})
.catch(err => {
console.log('error:' + err.response.data.message)
})
},
serverStatus() {
crudDeploy.serverStatus(JSON.stringify(this.currentRow))
.then(res => {
})
.catch(err => {
console.log('error:' + err.response.data.message)
})
},
initSelect() {
crudDeploy.getApps().then(res => {
this.apps = res.content
})
crudDeploy.getServers().then(res => {
this.servers = res.content
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,108 @@
<template>
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="系统还原" width="800px">
<!--工具栏-->
<div class="head-container">
<date-range-picker v-model="query.createTime" class="date-item" />
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
</div>
<el-form size="small" label-width="80px">
<!--表格渲染-->
<el-table v-loading="loading" :data="data" style="width: 100%" @row-click="showRow">
<el-table-column width="30px">
<template slot-scope="scope">
<el-radio v-model="radio" :label="scope.$index" />
</template>
</el-table-column>
<el-table-column prop="appName" label="应用名称" />
<el-table-column prop="ip" label="部署IP" />
<el-table-column prop="deployDate" label="部署时间" />
<el-table-column prop="deployUser" label="部署人员" />
</el-table>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="cancel">取消</el-button>
<el-button v-permission="['admin','deploy:add']" :loading="submitLoading" type="primary" @click="doSubmit">确认</el-button>
</div>
<!--分页组件-->
<el-pagination
:total="total"
:current-page="page + 1"
style="margin-top: 8px"
layout="total, prev, pager, next, sizes"
@size-change="sizeChange"
@current-change="pageChange"
/>
</el-dialog>
</template>
<script>
import crud from '@/mixins/crud'
import { reducte } from '@/api/mnt/deployHistory'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { DateRangePicker },
mixins: [crud],
props: {
appName: {
type: String,
default: ''
}
},
data() {
return {
submitLoading: false,
dialog: false,
history: [],
radio: '',
appNames: '',
selectIndex: ''
}
},
created() {
this.$nextTick(() => {
this.init()
})
},
methods: {
beforeInit() {
this.url = 'api/deployHistory'
this.deployId = this.$parent.deployId
if (this.deployId === '') {
return false
}
this.sort = 'deployDate,desc'
this.params['deployId'] = this.deployId
return true
},
showRow(row) {
this.radio = this.data.indexOf(row)
this.selectIndex = row.id
},
cancel() {
this.dialog = false
this.submitLoading = false
},
doSubmit() {
if (this.selectIndex === '') {
this.$message.error('请选择要还原的备份')
} else {
this.submitLoading = true
reducte(JSON.stringify(this.data[this.radio]))
.then(res => {
this.dialog = false
this.submitLoading = false
this.appNames = ''
this.$parent.crud.toQuery()
})
.catch(err => {
this.submitLoading = false
console.log('error:' + err.response.data.message)
})
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,93 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable placeholder="输入搜索内容" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.deployDate" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="appName" label="应用名称" />
<el-table-column prop="ip" label="部署IP" />
<el-table-column prop="deployUser" label="部署人员" />
<el-table-column prop="deployDate" label="部署时间" />
<el-table-column v-if="checkPer(['admin','deployHistory:del'])" label="操作" width="100px" align="center">
<template slot-scope="scope">
<el-popover
:ref="scope.row.id"
v-permission="['admin','deployHistory:del']"
placement="top"
width="180"
>
<p>确定删除本条数据吗</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
</div>
<el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" />
</el-popover>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import { del } from '@/api/mnt/deployHistory'
import CRUD, { presenter, header } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
export default {
name: 'DeployHistory',
components: { pagination, crudOperation, rrOperation, DateRangePicker },
cruds() {
return CRUD({ title: '部署历史', url: 'api/deployHistory', crudMethod: { del }})
},
mixins: [presenter(), header()],
data() {
return {
delLoading: false,
permission: {
del: ['admin', 'deployHistory:del']
}
}
},
created() {
this.crud.optShow = {
add: false,
edit: false,
del: true,
download: true
}
},
methods: {
delMethod(id) {
this.delLoading = true
del([id]).then(() => {
this.delLoading = false
this.$refs[id].doClose()
this.crud.dleChangePage(1)
this.crud.delSuccessNotify()
this.crud.toQuery()
}).catch(() => {
this.delLoading = false
this.$refs[id].doClose()
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,136 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.id" clearable placeholder="输入名称或IP搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="470px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="55px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" style="width: 370px" />
</el-form-item>
<el-form-item label="IP" prop="ip">
<el-input v-model="form.ip" style="width: 370px" />
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input-number v-model.number="form.port" controls-position="right" style="width: 370px;" />
</el-form-item>
<el-form-item label="账号" prop="account">
<el-input v-model="form.account" style="width: 370px" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" style="width: 200px" />
<el-button :loading="loading" type="success" style="align: right;" @click="testConnectServer">测试连接</el-button>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="ip" label="IP" />
<el-table-column prop="port" label="端口" />
<el-table-column prop="account" label="账号" />
<el-table-column prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','serverDeploy:edit','serverDeploy:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudServer from '@/api/mnt/serverDeploy'
import { testServerConnect } from '@/api/mnt/connect'
import { validateIP } from '@/utils/validate'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, name: null, ip: null, port: 22, account: 'root', password: null }
export default {
name: 'Server',
components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '服务器', url: 'api/serverDeploy', crudMethod: { ...crudServer }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
accountList: [],
accountMap: {},
loading: false,
permission: {
add: ['admin', 'serverDeploy:add'],
edit: ['admin', 'serverDeploy:edit'],
del: ['admin', 'serverDeploy:del']
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
ip: [
{ required: true, message: '请输入IP', trigger: 'blur' },
{ validator: validateIP, trigger: 'change' }
],
port: [
{ required: true, message: '请输入端口', trigger: 'blur', type: 'number' }
],
account: [
{ required: true, message: '请输入账号', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
]
}
}
},
methods: {
testConnectServer() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
testServerConnect(this.form).then((res) => {
this.loading = false
this.$notify({
title: res ? '连接成功' : '连接失败',
type: res ? 'success' : 'error',
duration: 2500
})
}).catch(() => {
this.loading = false
})
}
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>

View File

@@ -0,0 +1,135 @@
<template>
<div class="app-container">
<div class="head-container">
<Search />
<crudOperation>
<el-button
slot="left"
class="filter-item"
type="danger"
icon="el-icon-delete"
size="mini"
:loading="crud.delAllLoading"
@click="confirmDelAll()"
>
清空
</el-button>
</crudOperation>
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="请求方法">
<span>{{ props.row.method }}</span>
</el-form-item>
<el-form-item label="请求参数">
<span>{{ props.row.params }}</span>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column prop="username" label="用户名" />
<el-table-column prop="requestIp" label="IP" />
<el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
<el-table-column prop="description" label="描述" />
<el-table-column prop="browser" label="浏览器" />
<el-table-column prop="createTime" label="创建日期" />
<el-table-column label="异常详情" width="100px">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="info(scope.row.id)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog :visible.sync="dialog" title="异常详情" append-to-body top="30px" width="85%">
<pre>{{ errorInfo }}</pre>
</el-dialog>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import { getErrDetail, delAllError } from '@/api/monitor/log'
import Search from './search'
import CRUD, { presenter } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
export default {
name: 'ErrorLog',
components: { Search, crudOperation, pagination },
cruds() {
return CRUD({ title: '异常日志', url: 'api/logs/error' })
},
mixins: [presenter()],
data() {
return {
errorInfo: '', dialog: false
}
},
created() {
this.crud.optShow = {
add: false,
edit: false,
del: false,
download: true
}
},
methods: {
// 获取异常详情
info(id) {
this.dialog = true
getErrDetail(id).then(res => {
this.errorInfo = res.exception
})
},
confirmDelAll() {
this.$confirm(`确认清空所有异常日志吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.crud.delAllLoading = true
delAllError().then(res => {
this.crud.delAllLoading = false
this.crud.dleChangePage(1)
this.crud.delSuccessNotify()
this.crud.toQuery()
}).catch(err => {
this.crud.delAllLoading = false
console.log(err.response.data.message)
})
}).catch(() => {
})
}
}
}
</script>
<style scoped>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 70px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 100%;
}
.demo-table-expand .el-form-item__content {
font-size: 12px;
}
/deep/ .el-dialog__body {
padding: 0 20px 10px 20px !important;
}
.java.hljs {
color: #444;
background: #ffffff !important;
height: 630px !important;
}
</style>

View File

@@ -0,0 +1,114 @@
<template>
<div class="app-container">
<div class="head-container">
<Search />
<crudOperation>
<el-button
slot="left"
class="filter-item"
type="danger"
icon="el-icon-delete"
size="mini"
:loading="crud.delAllLoading"
@click="confirmDelAll()"
>
清空
</el-button>
</crudOperation>
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="请求方法">
<span>{{ props.row.method }}</span>
</el-form-item>
<el-form-item label="请求参数">
<span>{{ props.row.params }}</span>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column prop="username" label="用户名" />
<el-table-column prop="requestIp" label="IP" />
<el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
<el-table-column prop="description" label="描述" />
<el-table-column prop="browser" label="浏览器" />
<el-table-column prop="time" label="请求耗时" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
<el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
<el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建日期" width="180px" />
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import Search from './search'
import { delAllInfo } from '@/api/monitor/log'
import CRUD, { presenter } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
export default {
name: 'Log',
components: { Search, crudOperation, pagination },
cruds() {
return CRUD({ title: '日志', url: 'api/logs' })
},
mixins: [presenter()],
created() {
this.crud.optShow = {
add: false,
edit: false,
del: false,
download: true
}
},
methods: {
confirmDelAll() {
this.$confirm(`确认清空所有操作日志吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.crud.delAllLoading = true
delAllInfo().then(res => {
this.crud.delAllLoading = false
this.crud.dleChangePage(1)
this.crud.delSuccessNotify()
this.crud.toQuery()
}).catch(err => {
this.crud.delAllLoading = false
console.log(err.response.data.message)
})
}).catch(() => {
})
}
}
}
</script>
<style>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 70px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 100%;
}
.demo-table-expand .el-form-item__content {
font-size: 12px;
}
</style>

View File

@@ -0,0 +1,24 @@
<template>
<div v-if="crud.props.searchToggle">
<el-input
v-model="query.blurry"
clearable
size="small"
placeholder="请输入你要搜索的内容"
style="width: 200px;"
class="filter-item"
/>
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
</template>
<script>
import { header } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { rrOperation, DateRangePicker },
mixins: [header()]
}
</script>

View File

@@ -0,0 +1,121 @@
<template>
<div class="app-container">
<div class="head-container">
<div v-if="crud.props.searchToggle">
<el-input v-model="query.filter" clearable size="small" placeholder="全表模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation />
</div>
<crudOperation>
<el-button
slot="left"
class="filter-item"
type="danger"
icon="el-icon-delete"
size="mini"
:loading="delLoading"
:disabled="crud.selections.length === 0"
@click="doDelete(crud.selections)"
>
强退
</el-button>
</crudOperation>
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="userName" label="用户名" />
<el-table-column prop="nickName" label="用户昵称" />
<el-table-column prop="dept" label="部门" />
<el-table-column prop="ip" label="登录IP" />
<el-table-column :show-overflow-tooltip="true" prop="address" label="登录地点" />
<el-table-column prop="browser" label="浏览器" />
<el-table-column prop="loginTime" label="登录时间" />
<el-table-column label="操作" width="70px" fixed="right">
<template slot-scope="scope">
<el-popover
:ref="scope.$index"
v-permission="['admin']"
placement="top"
width="180"
>
<p>确定强制退出该用户吗</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs[scope.$index].doClose()">取消</el-button>
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.key, scope.$index)">确定</el-button>
</div>
<el-button slot="reference" size="mini" type="text">强退</el-button>
</el-popover>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import { del } from '@/api/monitor/online'
import CRUD, { presenter, header, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
export default {
name: 'OnlineUser',
components: { pagination, crudOperation, rrOperation },
cruds() {
return CRUD({ url: 'auth/online', title: '在线用户' })
},
mixins: [presenter(), header(), crud()],
data() {
return {
delLoading: false,
permission: {}
}
},
created() {
this.crud.msg.del = '强退成功!'
this.crud.optShow = {
add: false,
edit: false,
del: false,
download: true
}
},
methods: {
doDelete(datas) {
this.$confirm(`确认强退选中的${datas.length}个用户?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.delMethod(datas)
}).catch(() => {})
},
// 踢出用户
delMethod(key, index) {
const ids = []
if (key instanceof Array) {
key.forEach(val => {
ids.push(val.key)
})
} else ids.push(key)
this.delLoading = true
del(ids).then(() => {
this.delLoading = false
if (this.$refs[index]) {
this.$refs[index].doClose()
}
this.crud.dleChangePage(1)
this.crud.delSuccessNotify()
this.crud.toQuery()
}).catch(() => {
this.delLoading = false
if (this.$refs[index]) {
this.$refs[index].doClose()
}
})
}
}
}
</script>

View File

@@ -0,0 +1,291 @@
<template>
<div v-loading="!show" element-loading-text="数据加载中..." :style="!show ? 'height: 500px' : 'height: 100%'" class="app-container">
<div v-if="show">
<el-card class="box-card">
<div style="color: #666;font-size: 13px;">
<svg-icon icon-class="system" style="margin-right: 5px" />
<span>
系统{{ data.sys.os }}
</span>
<span>
IP{{ data.sys.ip }}
</span>
<span>
项目已不间断运行{{ data.sys.day }}
</span>
<i class="el-icon-refresh" style="margin-left: 40px" @click="init" />
</div>
</el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">状态</span>
</div>
<div>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">CPU使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
{{ data.cpu.name }}
</div>
<div style="padding: 3px">
{{ data.cpu.package }}
</div>
<div style="padding: 3px">
{{ data.cpu.core }}
</div>
<div style="padding: 3px">
{{ data.cpu.logic }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.cpu.used)" />
</div>
</el-tooltip>
<div class="footer">{{ data.cpu.coreNumber }} 核心</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">内存使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
总量{{ data.memory.total }}
</div>
<div style="padding: 3px">
已使用{{ data.memory.used }}
</div>
<div style="padding: 3px">
空闲{{ data.memory.available }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.memory.usageRate)" />
</div>
</el-tooltip>
<div class="footer">{{ data.memory.used }} / {{ data.memory.total }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">交换区使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
总量{{ data.swap.total }}
</div>
<div style="padding: 3px">
已使用{{ data.swap.used }}
</div>
<div style="padding: 3px">
空闲{{ data.swap.available }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.swap.usageRate)" />
</div>
</el-tooltip>
<div class="footer">{{ data.swap.used }} / {{ data.swap.total }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">磁盘使用率</div>
<div class="content">
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px">
总量{{ data.disk.total }}
</div>
<div style="padding: 3px">
空闲{{ data.disk.available }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.disk.usageRate)" />
</div>
</el-tooltip>
</div>
<div class="footer">{{ data.disk.used }} / {{ data.disk.total }}</div>
</el-col>
</div>
</el-card>
<div>
<el-row :gutter="6">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">CPU使用率监控</span>
</div>
<div>
<v-chart :options="cpuInfo" />
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">内存使用率监控</span>
</div>
<div>
<v-chart :options="memoryInfo" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<script>
import ECharts from 'vue-echarts'
import 'echarts/lib/chart/line'
import 'echarts/lib/component/polar'
import { initData } from '@/api/data'
export default {
name: 'ServerMonitor',
components: {
'v-chart': ECharts
},
data() {
return {
show: false,
monitor: null,
url: 'api/monitor',
data: {},
cpuInfo: {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
min: 0,
max: 100,
interval: 20
},
series: [{
data: [],
type: 'line',
areaStyle: {
normal: {
color: 'rgb(32, 160, 255)' // 改变区域颜色
}
},
itemStyle: {
normal: {
color: '#6fbae1',
lineStyle: {
color: '#6fbae1' // 改变折线颜色
}
}
}
}]
},
memoryInfo: {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
min: 0,
max: 100,
interval: 20
},
series: [{
data: [],
type: 'line',
areaStyle: {
normal: {
color: 'rgb(32, 160, 255)' // 改变区域颜色
}
},
itemStyle: {
normal: {
color: '#6fbae1',
lineStyle: {
color: '#6fbae1' // 改变折线颜色
}
}
}
}]
}
}
},
created() {
this.init()
this.monitor = window.setInterval(() => {
setTimeout(() => {
this.init()
}, 2)
}, 3500)
},
destroyed() {
clearInterval(this.monitor)
},
methods: {
init() {
initData(this.url, {}).then(data => {
this.data = data
this.show = true
if (this.cpuInfo.xAxis.data.length >= 8) {
this.cpuInfo.xAxis.data.shift()
this.memoryInfo.xAxis.data.shift()
this.cpuInfo.series[0].data.shift()
this.memoryInfo.series[0].data.shift()
}
this.cpuInfo.xAxis.data.push(data.time)
this.memoryInfo.xAxis.data.push(data.time)
this.cpuInfo.series[0].data.push(parseFloat(data.cpu.used))
this.memoryInfo.series[0].data.push(parseFloat(data.memory.usageRate))
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .box-card {
margin-bottom: 5px;
span {
margin-right: 28px;
}
.el-icon-refresh {
margin-right: 10px;
float: right;
cursor:pointer;
}
}
.cpu, .memory, .swap, .disk {
width: 20%;
float: left;
padding-bottom: 20px;
margin-right: 5%;
}
.title {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-bottom: 16px;
}
.footer {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-top: -5px;
margin-bottom: 10px;
}
.content {
text-align: center;
margin-top: 5px;
margin-bottom: 5px;
}
</style>

View File

@@ -0,0 +1,16 @@
<template>
<elFrame :src="sqlApi" />
</template>
<script>
import { mapGetters } from 'vuex'
import elFrame from '@/components/Iframe/index'
export default {
name: 'Sql',
components: { elFrame },
computed: {
...mapGetters([
'sqlApi'
])
}
}
</script>

View File

@@ -0,0 +1,36 @@
<template>
<div class="app-container">
<el-alert :closable="false" title="三级菜单1" type="success" />
<el-form label-width="170px" style="margin-top: 20px">
<el-form-item label="三级菜单缓存功能测试区">
<el-input v-model="input" placeholder="请输入内容" style="width: 360px;" />
</el-form-item>
</el-form>
<div>
<blockquote class="my-blockquote"> 三级菜单缓存配置教程</blockquote>
<pre class="my-code">
1将前后端代码更新为最新版版本或对照提交记录修改,点击查看-> <a href="https://gitee.com/elunez/eladmin/commit/43d1a63577f9d5347924355708429a2d210e29f7" target="_blank">提交(1)</a><a href="https://gitee.com/elunez/eladmin/commit/46393875148fcca5eaa327d4073f72edb3752f5c" target="_blank">提交(2)</a><a href="https://gitee.com/elunez/eladmin-web/commit/c93c99d8921abbb2c52afc806635f5ca08d6bda8" target="_blank">提交(3)</a>
2 二级菜单 菜单类型 设置为 目录 级别并且原有的 组件路径 需要清空
3 三级菜单 菜单缓存 设置为 最后将 组件名称 填写正确
4具体设置可参考 菜单管理 多级菜单 配置进行进行相应的修改
</pre>
<blockquote class="my-blockquote">更多帮助</blockquote>
<pre class="my-code">QQ交流群一群891137268二群947578238三群659622532</pre>
</div>
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return {
input: ''
}
}
}
</script>
<style scoped>
.my-code a{
color:#009688;
}
</style>

View File

@@ -0,0 +1,5 @@
<template>
<div style="padding:30px;">
<el-alert :closable="false" title="三级菜单2" type="success" />
</div>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<div style="padding:30px;">
<el-alert :closable="false" title="二级菜单" />
</div>
</template>

View File

@@ -0,0 +1,177 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">订单号</label>
<el-input v-model="query.orderCode" clearable placeholder="订单号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">发送者电报号</label>
<el-input v-model="query.sendUserTelegramId" clearable placeholder="发送者电报号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">发送者名称</label>
<el-input v-model="query.sendUserName" clearable placeholder="发送者名称" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">发送者输赢状况</label>
<el-input v-model="query.sendUserStatus" clearable placeholder="发送者输赢状况" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">抢红包者电报号</label>
<el-input v-model="query.getUserTelegramId" clearable placeholder="抢红包者电报号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">抢红包者名称</label>
<el-input v-model="query.getUserName" clearable placeholder="抢红包者名称" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">抢红包者输赢状况</label>
<el-input v-model="query.getUserStatus" clearable placeholder="抢红包者输赢状况" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="订单号">
<el-input v-model="form.orderCode" style="width: 370px;" />
</el-form-item>
<el-form-item label="发包金额">
<el-input v-model="form.chipTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label="雷号">
<el-input v-model="form.bombNumber" style="width: 370px;" />
</el-form-item>
<el-form-item label="领取次数">
<el-input v-model="form.nowReceiveCount" style="width: 370px;" />
</el-form-item>
<el-form-item label="发送者电报号">
<el-input v-model="form.sendUserTelegramId" style="width: 370px;" />
</el-form-item>
<el-form-item label="发送者名称">
<el-input v-model="form.sendUserName" style="width: 370px;" />
</el-form-item>
<el-form-item label="发送者输赢状况">
<el-input v-model="form.sendUserStatus" style="width: 370px;" />
</el-form-item>
<el-form-item label="手续费">
<el-input v-model="form.sendUserFee" style="width: 370px;" />
</el-form-item>
<el-form-item label="赔率">
<el-input v-model="form.sendUserProfit" style="width: 370px;" />
</el-form-item>
<el-form-item label="抢红包者电报号">
<el-input v-model="form.getUserTelegramId" style="width: 370px;" />
</el-form-item>
<el-form-item label="抢红包者名称">
<el-input v-model="form.getUserName" style="width: 370px;" />
</el-form-item>
<el-form-item label="抢红包者输赢状况">
<el-input v-model="form.getUserStatus" style="width: 370px;" />
</el-form-item>
<el-form-item label="红包金额">
<el-input v-model="form.getUserGetAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="手续费">
<el-input v-model="form.getUserFee" style="width: 370px;" />
</el-form-item>
<el-form-item label="赔率">
<el-input v-model="form.getUserProfit" style="width: 370px;" />
</el-form-item>
<el-form-item label="specialPrizeType">
<el-input v-model="form.specialPrizeType" style="width: 370px;" />
</el-form-item>
<el-form-item label="specialPrizeAmount">
<el-input v-model="form.specialPrizeAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="createTime">
<el-input v-model="form.createTime" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="orderCode" label="订单号" />
<el-table-column prop="chipTotal" label="发包金额" />
<el-table-column prop="bombNumber" label="雷号" />
<el-table-column prop="nowReceiveCount" label="领取次数" />
<el-table-column prop="sendUserTelegramId" label="发送者电报号" />
<el-table-column prop="sendUserName" label="发送者名称" />
<el-table-column prop="sendUserStatus" label="发送者输赢状况" />
<el-table-column prop="sendUserFee" label="手续费" />
<el-table-column prop="sendUserProfit" label="赔率" />
<el-table-column prop="getUserTelegramId" label="抢红包者电报号" />
<el-table-column prop="getUserName" label="抢红包者名称" />
<el-table-column prop="getUserStatus" label="抢红包者输赢状况" />
<el-table-column prop="getUserGetAmount" label="红包金额" />
<el-table-column prop="getUserFee" label="手续费" />
<el-table-column prop="getUserProfit" label="赔率" />
<el-table-column prop="createTime" label="创建时间" />
<!-- <el-table-column v-if="checkPer(['admin','botSendOrderDetails:edit','botSendOrderDetails:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column> -->
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudBotSendOrderDetails from '@/api/botSendOrderDetails'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, orderCode: null, chipTotal: null, bombNumber: null, nowReceiveCount: null, sendUserTelegramId: null, sendUserName: null, sendUserStatus: null, sendUserFee: null, sendUserProfit: null, getUserTelegramId: null, getUserName: null, getUserStatus: null, getUserGetAmount: null, getUserFee: null, getUserProfit: null, specialPrizeType: null, specialPrizeAmount: null, createTime: null }
export default {
name: 'BotSendOrderDetails',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({ title: 'sendDetail', url: 'api/botSendOrderDetails', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBotSendOrderDetails }})
},
data() {
return {
permission: {
add: ['admin', 'botSendOrderDetails:add'],
edit: ['admin', 'botSendOrderDetails:edit'],
del: ['admin', 'botSendOrderDetails:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'orderCode', display_name: '订单号' },
{ key: 'sendUserTelegramId', display_name: '发送者电报号' },
{ key: 'sendUserName', display_name: '发送者名称' },
{ key: 'sendUserStatus', display_name: '发送者输赢状况' },
{ key: 'getUserTelegramId', display_name: '抢红包者电报号' },
{ key: 'getUserName', display_name: '抢红包者名称' },
{ key: 'getUserStatus', display_name: '抢红包者输赢状况' }
]
}
},
created() {
this.crud.optShow = {
add:false,
edit: false,
reset: true,
download: true
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

176
src/views/order/index.vue Normal file
View File

@@ -0,0 +1,176 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">订单号</label>
<el-input v-model="query.orderCode" clearable placeholder="订单号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">用户电报号</label>
<el-input v-model="query.userTelegramId" clearable placeholder="用户电报号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">用户名称</label>
<el-input v-model="query.userName" clearable placeholder="用户名称" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">状态</label>
<el-input v-model="query.status" clearable placeholder="状态" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">类型</label>
<el-input v-model="query.type" clearable placeholder="类型" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="订单号">
<el-input v-model="form.orderCode" style="width: 370px;" />
</el-form-item>
<el-form-item label="消息id">
<el-input v-model="form.messageId" style="width: 370px;" />
</el-form-item>
<el-form-item label="聊天id">
<el-input v-model="form.chatId" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户电报号">
<el-input v-model="form.userTelegramId" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户名称">
<el-input v-model="form.userName" style="width: 370px;" />
</el-form-item>
<el-form-item label="赔率">
<el-input v-model="form.odds" style="width: 370px;" />
</el-form-item>
<el-form-item label="发包金额">
<el-input v-model="form.chipTotal" style="width: 370px;" />
</el-form-item>
<el-form-item label="未抢完金额">
<el-input v-model="form.chipRemain" style="width: 370px;" />
</el-form-item>
<el-form-item label="总盈利">
<el-input v-model="form.profit" style="width: 370px;" />
</el-form-item>
<el-form-item label="雷号">
<el-input v-model="form.bombNumber" style="width: 370px;" />
</el-form-item>
<el-form-item label="领取次数">
<el-input v-model="form.nowReceiveCount" style="width: 370px;" />
</el-form-item>
<el-form-item label="状态">
<el-input v-model="form.status" style="width: 370px;" />
</el-form-item>
<el-form-item label="类型">
<el-input v-model="form.type" style="width: 370px;" />
</el-form-item>
<el-form-item label="创建时间">
<el-input v-model="form.createTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="更新时间">
<el-input v-model="form.updateTime" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="id" />
<el-table-column prop="orderCode" label="订单号" />
<el-table-column prop="messageId" label="消息id" />
<el-table-column prop="chatId" label="聊天id" />
<el-table-column prop="userTelegramId" label="用户电报号" />
<el-table-column prop="userName" label="用户名称" />
<el-table-column prop="odds" label="赔率" />
<el-table-column prop="chipTotal" label="发包金额" />
<el-table-column prop="chipRemain" label="未抢完金额" />
<el-table-column prop="profit" label="总盈利" />
<el-table-column prop="bombNumber" label="雷号" />
<el-table-column prop="nowReceiveCount" label="领取次数" />
<el-table-column prop="status" label="状态">
<template slot-scope="scope">
{{ dict.label.order_status[scope.row.status] }}
</template>
</el-table-column>
<el-table-column prop="type" label="类型">
<template slot-scope="scope">
{{ dict.label.order_type[scope.row.type] }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="updateTime" label="更新时间" />
<!-- <el-table-column v-if="checkPer(['admin','botSendOrder:edit','botSendOrder:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="crud.toQuery">查看详情</el-button>
</template>
</el-table-column> -->
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudBotSendOrder from '@/api/botSendOrder'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, orderCode: null, messageId: null, chatId: null, userTelegramId: null, userName: null, odds: null, chipTotal: null, chipRemain: null, profit: null, bombNumber: null, nowReceiveCount: null, status: null, type: null, btcPrice: null, gongZhengNum: null, createTime: null, updateTime: null }
export default {
name: 'BotSendOrder',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['order_status', 'order_type'],
cruds() {
return CRUD({ title: 'OrderController', url: 'api/botSendOrder', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBotSendOrder }})
},
data() {
return {
permission: {
add: ['admin', 'botSendOrder:add'],
edit: ['admin', 'botSendOrder:edit'],
del: ['admin', 'botSendOrder:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'orderCode', display_name: '订单号' },
{ key: 'userTelegramId', display_name: '用户电报号' },
{ key: 'userName', display_name: '用户名称' },
{ key: 'status', display_name: '状态' },
{ key: 'type', display_name: '类型' }
]
}
},
created() {
this.crud.optShow = {
add:false,
edit: false,
reset: true,
download: true
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
},
queryOrderDetail(id){
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,224 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">id</label>
<el-input v-model="query.id" clearable placeholder="id" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">订单编号</label>
<el-input v-model="query.orderNo" clearable placeholder="订单编号" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">商品售价</label>
<el-input v-model="query.productAmount" clearable placeholder="商品售价" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">支付金额</label>
<el-input v-model="query.payAmount" clearable placeholder="支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">现金支付金额</label>
<el-input v-model="query.cashPaidAmount" clearable placeholder="现金支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">微信支付金额</label>
<el-input v-model="query.wxPaidAmount" clearable placeholder="微信支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">支付宝支付金额</label>
<el-input v-model="query.aliPaidAmount" clearable placeholder="支付宝支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">储值支付金额</label>
<el-input v-model="query.depositPaidAmount" clearable placeholder="储值支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">银联支付金额</label>
<el-input v-model="query.bankPaidAmount" clearable placeholder="银联支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">虚拟支付金额</label>
<el-input v-model="query.virtualPaidAmount" clearable placeholder="虚拟支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">其他支付金额</label>
<el-input v-model="query.otherPaidAmount" clearable placeholder="其他支付金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">发货类型</label>
<el-input v-model="query.sendType" clearable placeholder="发货类型" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">状态</label>
<el-input v-model="query.status" clearable placeholder="状态" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">店铺Id</label>
<el-input v-model="query.shopId" clearable placeholder="店铺Id" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">商户会员Id</label>
<el-input v-model="query.memberId" clearable placeholder="商户会员Id" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">用户Id</label>
<el-input v-model="query.userId" clearable placeholder="用户Id" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">支付时间</label>
<el-input v-model="query.paidTime" clearable placeholder="支付时间" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">createdAt</label>
<el-input v-model="query.createdAt" clearable placeholder="createdAt" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">收银台是否已接单</label>
<el-input v-model="query.isAccepted" clearable placeholder="收银台是否已接单" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="订单编号">
<el-input v-model="form.orderNo" style="width: 370px;" />
</el-form-item>
<el-form-item label="商品售价">
<el-input v-model="form.productAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="支付金额">
<el-input v-model="form.payAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="现金支付金额">
<el-input v-model="form.cashPaidAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="微信支付金额">
<el-input v-model="form.wxPaidAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="支付宝支付金额">
<el-input v-model="form.aliPaidAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="储值支付金额">
<el-input v-model="form.depositPaidAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="银联支付金额">
<el-input v-model="form.bankPaidAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="虚拟支付金额">
<el-input v-model="form.virtualPaidAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="其他支付金额">
<el-input v-model="form.otherPaidAmount" style="width: 370px;" />
</el-form-item>
<el-form-item label="发货类型">
<el-input v-model="form.sendType" style="width: 370px;" />
</el-form-item>
<el-form-item label="状态">
<el-input v-model="form.status" style="width: 370px;" />
</el-form-item>
<el-form-item label="店铺Id">
<el-input v-model="form.shopId" style="width: 370px;" />
</el-form-item>
<el-form-item label="商户会员Id">
<el-input v-model="form.memberId" style="width: 370px;" />
</el-form-item>
<el-form-item label="用户Id">
<el-input v-model="form.userId" style="width: 370px;" />
</el-form-item>
<el-form-item label="支付时间">
<el-input v-model="form.paidTime" style="width: 370px;" />
</el-form-item>
<el-form-item label="createdAt">
<el-input v-model="form.createdAt" style="width: 370px;" />
</el-form-item>
<el-form-item label="收银台是否已接单">
<el-input v-model="form.isAccepted" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="id" />
<el-table-column prop="orderNo" label="订单编号" />
<el-table-column prop="productAmount" label="商品售价" />
<el-table-column prop="payAmount" label="支付金额" />
<el-table-column prop="cashPaidAmount" label="现金支付金额" />
<el-table-column prop="wxPaidAmount" label="微信支付金额" />
<el-table-column prop="aliPaidAmount" label="支付宝支付金额" />
<el-table-column prop="depositPaidAmount" label="储值支付金额" />
<el-table-column prop="bankPaidAmount" label="银联支付金额" />
<el-table-column prop="virtualPaidAmount" label="虚拟支付金额" />
<el-table-column prop="otherPaidAmount" label="其他支付金额" />
<el-table-column prop="sendType" label="发货类型">
<template slot-scope="scope">
{{ dict.label.order_send_type[scope.row.sendType] }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态">
<template slot-scope="scope">
{{ dict.label.order_status[scope.row.status] }}
</template>
</el-table-column>
<el-table-column prop="shopId" label="店铺Id" />
<el-table-column prop="memberId" label="商户会员Id" />
<el-table-column prop="userId" label="用户Id" />
<el-table-column prop="paidTime" label="支付时间" />
<el-table-column prop="createdAt" label="createdAt" />
<el-table-column prop="isAccepted" label="收银台是否已接单">
<template slot-scope="scope">
{{ dict.label.is_accepted[scope.row.isAccepted] }}
</template>
</el-table-column>
<el-table-column v-if="checkPer(['admin','tbOrderInfo:edit','tbOrderInfo:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudTbOrderInfo from '@/api/tbOrderInfo'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, orderNo: null, settlementAmount: null, packFee: null, originAmount: null, productAmount: null, amount: null, refundAmount: null, payAmount: null, freightAmount: null, cashPaidAmount: null, wxPaidAmount: null, aliPaidAmount: null, depositPaidAmount: null, bankPaidAmount: null, virtualPaidAmount: null, otherPaidAmount: null, discountAmount: null, tableId: null, smallChange: null, sendType: null, orderType: null, productType: null, status: null, billingId: null, merchantId: null, shopId: null, isVip: null, memberId: null, userId: null, productScore: null, deductScore: null, userCouponId: null, userCouponAmount: null, isMaster: null, masterId: null, refundAble: null, paidTime: null, isEffect: null, isGroup: null, updatedAt: null, systemTime: null, createdAt: null, isAccepted: null }
export default {
name: 'TbOrderInfo',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['order_send_type', 'order_status', 'is_accepted'],
cruds() {
return CRUD({ title: '/orderInfo', url: 'api/tbOrderInfo', idField: 'id', sort: 'id,desc', crudMethod: { ...crudTbOrderInfo }})
},
data() {
return {
permission: {
add: ['admin', 'tbOrderInfo:add'],
edit: ['admin', 'tbOrderInfo:edit'],
del: ['admin', 'tbOrderInfo:del']
},
rules: {
},
queryTypeOptions: [
{ key: 'id', display_name: 'id' },
{ key: 'orderNo', display_name: '订单编号' },
{ key: 'productAmount', display_name: '商品售价' },
{ key: 'payAmount', display_name: '支付金额' },
{ key: 'cashPaidAmount', display_name: '现金支付金额' },
{ key: 'wxPaidAmount', display_name: '微信支付金额' },
{ key: 'aliPaidAmount', display_name: '支付宝支付金额' },
{ key: 'depositPaidAmount', display_name: '储值支付金额' },
{ key: 'bankPaidAmount', display_name: '银联支付金额' },
{ key: 'virtualPaidAmount', display_name: '虚拟支付金额' },
{ key: 'otherPaidAmount', display_name: '其他支付金额' },
{ key: 'sendType', display_name: '发货类型' },
{ key: 'status', display_name: '状态' },
{ key: 'shopId', display_name: '店铺Id' },
{ key: 'memberId', display_name: '商户会员Id' },
{ key: 'userId', display_name: '用户Id' },
{ key: 'paidTime', display_name: '支付时间' },
{ key: 'createdAt', display_name: 'createdAt' },
{ key: 'isAccepted', display_name: '收银台是否已接单' }
]
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,361 @@
<template>
<el-drawer
title="订单详情"
size="50%"
:visible.sync="drawer"
direction="rtl"
v-loading="loading"
>
<div class="header">
<div class="title">收银订单</div>
<div class="table">
<div class="item">
<div class="t">订单状态</div>
<div class="b">
<el-tag :type="detail.status == 'closed' ? 'success' : 'warning'">{{
detail.status | statusFilter
}}</el-tag>
</div>
</div>
<div class="item">
<div class="t">订单金额</div>
<div class="b">{{ detail.orderAmount }}</div>
</div>
<div class="item">
<div class="t">订单时间</div>
<div class="b">
{{ detail.createdAt | timeFilter }}
</div>
</div>
</div>
</div>
<div class="container">
<el-tabs v-model="type" @tab-click="getTableData">
<el-tab-pane label="基本信息" name="1">
<div class="info_content">
<div class="item">
<div class="label">会员信息</div>
<div class="row">
<div>会员昵称-</div>
<div>联系电话-</div>
</div>
</div>
<div class="item">
<div class="label">收款详情</div>
<div class="row">
<!-- <div>商品金额{{ detail.productAmount }}</div> -->
<div>打包费{{ detail.packFee || "-" }}</div>
</div>
<div class="row">
<div>订单原价{{ detail.originAmount }}</div>
<div>优惠金额{{ detail.userCouponAmount || "-" }}</div>
<div>
实收金额<span style="color: red;"
>{{ detail.payAmount }}</span
>
</div>
</div>
<div class="row">
<div>
退款金额{{ detail.refundAmount }}
<span
style="color: #FF9731;cursor: pointer;"
v-if="detail.isRefund"
@click="type = '3'"
>退款详情></span
>
</div>
<div>支付方式{{ detail.payType }}</div>
</div>
</div>
<div class="item">
<div class="label">订单信息</div>
<div class="row">
<div>订单编号{{ detail.orderNo }}</div>
<div>订单类型{{ detail.sendType | sendTypeFilter }}</div>
<div>创建时间{{ detail.createdAt | timeFilter }}</div>
</div>
<div class="row">
<div>设备名称-</div>
<div>支付时间{{ detail.paidTime | timeFilter }}</div>
<div>更新时间{{ detail.updatedAt | timeFilter }}</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="商品信息" name="2">
<el-table :data="detail.detailList">
<el-table-column label="商品">
<template v-slot="scope">
<div class="shop_info">
<el-image
:src="scope.row.productImg"
style="width: 40px;height: 40px;"
></el-image>
<div class="info">
<span>{{ scope.row.productName }}</span>
<span style="color: #999;">{{
scope.row.productSkuName
}}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="数量">
<template v-slot="scope">
x{{ scope.row.num }}
</template>
</el-table-column>
<el-table-column label="单价">
<template v-slot="scope">
{{ scope.row.price }}/
</template>
</el-table-column>
<el-table-column label="小计">
<template v-slot="scope">
{{ scope.row.priceAmount }}
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="退款详情" name="3" v-if="detail.isRefund">
<div class="refund_wrap">
<div class="row" v-for="item in refoundList" :key="item.id">
<div class="time">{{ item.createdAt | timeFilter }}</div>
<div class="list">
<div class="list_row" v-for="val in item.detailList">
<div class="item">
<el-image
:src="val.productImg"
style="width: 50px;height: 50px;"
></el-image>
<span class="name">{{ val.productName }}</span>
</div>
<div class="item">
<span>退-{{ val.num }}</span>
</div>
<div class="item">
<span>{{ val.priceAmount }}</span>
</div>
<div class="item">
<span>-{{ val.priceAmount }}</span>
</div>
</div>
</div>
<div class="foot">退款-{{ item.refundAmount }}</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</el-drawer>
</template>
<script>
import orderEnum from "../orderEnum";
import dayjs from "dayjs";
import { tbOrderInfoDetail, tbOrderInfoData } from "@/api/order";
export default {
data() {
return {
orderEnum,
drawer: false,
type: "1",
detail: "",
loading: false,
refoundList: []
};
},
filters: {
orderTypeFilter(t) {
if (t) {
return t && orderEnum.orderType.find(item => item.key == t).label;
} else {
return t;
}
},
sendTypeFilter(t) {
if (t) {
return orderEnum.sendType.find(item => item.key == t).label;
} else {
return t;
}
},
statusFilter(t) {
if (t) {
return t && orderEnum.status.find(item => item.key == t).label;
} else {
return t;
}
},
timeFilter(t) {
if (t) {
return dayjs(t).format("YYYY-MM-DD HH:mm:ss");
} else {
return "-";
}
}
},
methods: {
// 切换类型
getTableData() {
if (this.type == "3") {
this.tbOrderInfoData();
}
},
// 获取退单列表
async tbOrderInfoData() {
try {
const res = await tbOrderInfoData({
source: this.detail.id,
page: 0,
pageSize: 500,
orderType: "0"
});
this.refoundList = res.content;
} catch (error) {
console.log(error);
}
},
// 获取订单详情
async tbOrderInfoDetail(id) {
try {
this.loading = true;
const res = await tbOrderInfoDetail(id);
this.detail = res;
this.loading = false;
} catch (error) {
console.log(error);
}
},
show(obj) {
this.drawer = true;
this.type = "1";
this.detail = "";
this.tbOrderInfoDetail(obj.id);
}
}
};
</script>
<style scoped lang="scss">
.shop_info {
display: flex;
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 6px;
}
}
.header {
padding: 0 20px 0;
.title {
font-size: 20px;
color: #ff9731;
}
.table {
display: flex;
padding: 20px 0;
.item {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
font-size: 14px;
.b {
padding-top: 20px;
}
}
}
}
.container {
padding: 0 20px;
font-size: 14px;
.info_content {
padding: 20px 0;
.item {
border-bottom: 1px dashed #ececec;
padding-bottom: 20px;
&:not(:first-child) {
margin-top: 20px;
}
.label {
position: relative;
padding-left: 20px;
color: #333;
&::after {
content: "";
width: 4px;
height: 100%;
position: absolute;
left: 0;
top: 0;
background-color: #1890ff;
}
}
.row {
display: flex;
color: #555;
padding-top: 20px;
div {
width: 33.333%;
}
}
}
}
.refund_wrap {
.row {
border-bottom: 1px dashed #ececec;
padding-bottom: 20px;
&:not(:first-child) {
margin-top: 20px;
}
.time {
font-weight: bold;
color: #333;
}
.list {
.list_row {
display: flex;
padding-top: 10px;
.item {
flex: 1;
display: flex;
color: #555;
.name {
margin-left: 6px;
}
}
}
}
.foot {
color: #333;
display: flex;
justify-content: flex-end;
}
}
}
}
</style>

View File

@@ -0,0 +1,84 @@
export default {
status: [
{
key: 'unpaid',
label: '待支付'
},
{
key: 'unsend',
label: '待发货'
},
{
key: 'closed',
label: '订单完成'
},
{
key: 'send',
label: '已发'
},
{
key: 'refunding',
label: '申请退单'
},
{
key: 'refund',
label: '退单'
},
{
key: 'cancelled',
label: '取消订单'
},
{
key: 'merge',
label: '合台'
},
{
key: 'pending',
label: '挂单'
},
{
key: 'activate',
label: '激活'
},
{
key: 'paying',
label: '支付中'
}
],
sendType: [
{
key: 'post',
label: '快递'
},
{
key: 'takeaway',
label: '外卖'
},
{
key: 'takeself',
label: '自提'
},
{
key: 'table',
label: '堂食'
}
],
orderType: [
{
key: 'cash',
label: '收银'
},
{
key: 'miniapp',
label: '小程序'
},
{
key: 'offline',
label: '线下'
},
{
key: 'return',
label: '退单'
}
]
}

View File

@@ -0,0 +1,532 @@
<template>
<div class="app-container">
<el-tabs v-model="orderType" @tab-click="getTableData">
<el-tab-pane label="全部订单" name=""></el-tab-pane>
<el-tab-pane label="收银订单" name="cash"></el-tab-pane>
<el-tab-pane label="小程序订单" name="miniapp"></el-tab-pane>
</el-tabs>
<div class="head-container">
<el-form :model="query" label-width="100px" label-position="left">
<el-form-item label="订单状态">
<el-radio-group v-model="query.status">
<el-radio-button label="">全部</el-radio-button>
<el-radio-button
:label="item.key"
v-for="item in orderEnum.status"
:key="item.key"
>
{{ item.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="支付方式">
<el-radio-group v-model="query.payType">
<el-radio-button label="">全部</el-radio-button>
<el-radio-button
:label="item.payType"
v-for="item in payTypes"
:key="item.payType"
>
{{ item.payName }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="创建时间">
<el-radio-group v-model="timeValue" @change="timeChange">
<el-radio-button label="">全部</el-radio-button>
<el-radio-button label="0">今天</el-radio-button>
<el-radio-button label="-1">昨天</el-radio-button>
<el-radio-button label="-7">最近7天</el-radio-button>
<el-radio-button label="-30">最近30天</el-radio-button>
<el-radio-button label="week">本周</el-radio-button>
<el-radio-button label="month">本月</el-radio-button>
<el-radio-button label="custom">自定义</el-radio-button>
</el-radio-group>
<el-date-picker
v-model="query.createdAt"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
value-format="yyyy-MM-dd HH:mm:ss"
v-if="timeValue == 'custom'"
>
</el-date-picker>
</el-form-item>
<el-form-item label="订单号">
<el-input
v-model="query.orderNo"
placeholder="请输入订单号"
style="width: 300px;"
></el-input>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
<el-button
icon="el-icon-download"
v-loading="downloadLoading"
@click="downloadHandle"
>
<span v-if="!downloadLoading">导出Excel</span>
<span v-else>下载中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<div class="collect_wrap">
<div class="item">
<div class="icon_wrap" style="--bg-color:#C978EE">
<i class="icon el-icon-s-goods"></i>
</div>
<div class="info">
<div class="m">{{ payCountTotal }}</div>
<div class="t">总金额</div>
</div>
</div>
<div class="item" v-for="item in payCountList" :key="item.payType">
<div class="icon_wrap" style="--bg-color:#fff">
<el-image class="img" :src="item.icon"></el-image>
</div>
<div class="info">
<div class="m">{{ item.payAmount || 0 }}</div>
<div class="t">{{ item.payType }}</div>
</div>
</div>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.data" v-loading="tableData.loading">
<el-table-column label="订单号信息">
<template v-slot="scope">
<div class="table_order_info">
<div class="order_no">{{ scope.row.orderNo }}</div>
<!-- <div>会员 {{ scope.row.orderNo }}</div> -->
<div class="type">
{{ scope.row.orderType | orderTypeFilter }}
{{ scope.row.sendType | sendTypeFilter }}
</div>
</div>
</template>
</el-table-column>
<el-table-column label="商品信息">
<template v-slot="scope">
<div class="goods_info">
<div
class="row"
v-for="item in scope.row.detailList"
:key="item.id"
>
<el-image :src="item.productImg" class="cover" lazy></el-image>
<div class="info">
<div class="name">
{{ item.productName }}
<span
class="refund"
v-if="
item.status == 'refunding' || item.status == 'refund'
"
>(退 - {{ item.num }})</span
>
</div>
<div class="sku">{{ item.productSkuName }}</div>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="订单金额">
<template v-slot="scope">
<div>{{ scope.row.orderType | orderTypeFilter }}</div>
<div
class="refund"
v-if="
scope.row.status == 'refunding' || scope.row.status == 'refund'
"
>
退款:-¥{{ scope.row.refundAmount }}
</div>
<div>¥{{ scope.row.orderAmount }}</div>
</template>
</el-table-column>
<el-table-column label="状态">
<template v-slot="scope">
<template
v-if="
scope.row.status == 'refund' && scope.row.orderType != 'return'
"
>
<el-tag type="primary">已完成</el-tag>
</template>
<template v-else>
<el-tag type="primary" v-if="scope.row.status == 'closed'">
{{ scope.row.status | statusFilter }}
</el-tag>
<el-tag type="warning" v-if="scope.row.status != 'closed'">
{{ scope.row.status | statusFilter }}
</el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="创建时间">
<template v-slot="scope">
{{ scope.row.createdAt | timeFilter }}
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template v-slot="scope">
<el-button type="text" @click="$refs.orderDetail.show(scope.row)"
>详情</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination
:total="tableData.total"
:current-page="tableData.page + 1"
:page-size="tableData.size"
@current-change="paginationChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
</div>
<orderDetail ref="orderDetail" />
</div>
</template>
<script>
import orderEnum from "./orderEnum";
import { tbShopPayTypeGet } from "@/api/setting";
import { tbOrderInfoData, tbOrderInfoDownload, payCount } from "@/api/order";
import dayjs from "dayjs";
import { downloadFile } from "@/utils/index";
import orderDetail from "./components/orderDetail";
export default {
components: { orderDetail },
data() {
return {
orderEnum,
payTypes: [],
timeValue: "",
resetQuery: null,
orderType: "",
query: {
id: "",
status: "",
payType: "",
orderNo: "",
createdAt: []
},
tableData: {
data: [],
page: 0,
size: 10,
loading: false,
total: 0
},
downloadLoading: false,
payCountList: "",
payCountTotal: 0
};
},
filters: {
orderTypeFilter(t) {
if (t) {
return t && orderEnum.orderType.find(item => item.key == t).label;
} else {
return t;
}
},
sendTypeFilter(t) {
if (t) {
return orderEnum.sendType.find(item => item.key == t).label;
} else {
return t;
}
},
statusFilter(t) {
if (t) {
return t && orderEnum.status.find(item => item.key == t).label;
} else {
return t;
}
},
timeFilter(time) {
return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
}
},
mounted() {
this.resetQuery = { ...this.query };
this.tbShopPayTypeGet();
this.getTableData();
},
methods: {
// 获取订单汇总
async payCount() {
try {
const res = await payCount(this.query.createdAt);
this.payCountList = res;
let total = 0;
res.map(item => {
total += item.payAmount;
});
this.payCountTotal = Math.floor(total * 100) / 100;
} catch (error) {
console.log(error);
}
},
// 导出Excel
async downloadHandle() {
try {
this.downloadLoading = true;
const file = await tbOrderInfoDownload({
orderType: this.orderType,
shopId: localStorage.getItem("shopId"),
...this.query
});
downloadFile(file, "订单数据", "xlsx");
this.downloadLoading = false;
} catch (error) {
this.downloadLoading = false;
console.log(error);
}
},
// 重置查询
resetHandle() {
this.timeValue = "";
this.query = { ...this.resetQuery };
this.page = 0;
this.getTableData();
},
// 分页大小改变
sizeChange(e) {
this.tableData.size = e;
this.getTableData();
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1;
this.getTableData();
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true;
try {
this.payCount();
const res = await tbOrderInfoData({
page: this.tableData.page,
pageSize: this.tableData.size,
orderType: this.orderType,
...this.query
});
this.tableData.loading = false;
this.tableData.data = res.content;
this.tableData.total = res.totalElements;
} catch (error) {
console.log(error);
}
},
// 切换时间
timeChange(e) {
const format = ["YYYY-MM-DD 00:00:00", "YYYY-MM-DD 23:59:59"];
switch (e) {
case "":
// 全部
this.query.createdAt = [];
break;
case "0":
// 今天
this.query.createdAt = [
dayjs().format(format[0]),
dayjs().format(format[1])
];
break;
case "-1":
// 昨天
this.query.createdAt = [
dayjs()
.add(-1, "d")
.format(format[0]),
dayjs()
.add(-1, "d")
.format(format[1])
];
break;
case "-7":
// 最近7天
this.query.createdAt = [
dayjs()
.add(-7, "d")
.format(format[0]),
dayjs().format(format[1])
];
break;
case "-30":
// 最近7天
this.query.createdAt = [
dayjs()
.add(-30, "d")
.format(format[0]),
dayjs().format(format[1])
];
break;
case "week":
// 本周
this.query.createdAt = [
dayjs()
.startOf("week")
.format(format[0]),
dayjs()
.endOf("week")
.format(format[1])
];
break;
case "month":
// 本周
this.query.createdAt = [
dayjs()
.startOf("month")
.format(format[0]),
dayjs()
.endOf("month")
.format(format[1])
];
break;
case "custom":
// 自定义
this.query.createdAt = [];
break;
default:
break;
}
},
// 获取支付方式
async tbShopPayTypeGet() {
try {
const { content } = await tbShopPayTypeGet({
page: 0,
size: 100,
shopId: localStorage.getItem("shopId")
});
this.payTypes = content;
} catch (error) {
console.log(error);
}
}
}
};
</script>
<style scoped lang="scss">
.collect_wrap {
display: flex;
gap: 14px;
.item {
flex: 1;
display: flex;
align-items: center;
background-color: #f5f5f5;
padding: 20px;
.icon_wrap {
$size: 34px;
$border: 6px;
width: $size;
height: $size;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--bg-color);
border-radius: 50%;
position: relative;
&::after {
content: "";
width: $size + $border;
height: $size + $border;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--bg-color);
opacity: 0.3;
}
.icon {
font-size: 16px;
color: #fff;
}
.img {
width: 20px;
height: 20px;
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.m {
font-weight: bold;
}
.t {
font-size: 12px;
color: #999;
padding-top: 4px;
}
}
}
}
.refund {
color: #ff9731;
font-weight: bold;
}
.table_order_info {
.order_no {
color: #999;
}
.type {
color: #e6a23c;
}
}
.goods_info {
.row {
display: flex;
&:not(:first-child) {
margin-top: 10px;
}
.cover {
width: 40px;
height: 40px;
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.sku {
color: #999;
}
}
}
}
</style>

View File

@@ -0,0 +1,717 @@
<template>
<div class="app-container">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-form-item label="商品类型" prop="typeEnum">
<div class="shop_type_box" :class="{ disabled: form.id }">
<div class="item" v-for="(item, index) in shopTypes" :key="index"
:class="{ active: shopTypesActive == index }" @click="changeTypeEnum(index)">
<div class="s_title">{{ item.label }}</div>
<div class="intro">{{ item.intro }}</div>
<div class="active_dot">
<i class="el-icon-check"></i>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input v-model="form.name" placeholder="请输入商品名称" style="width: 500px;"></el-input>
</el-form-item>
<el-form-item label="单位">
<el-select v-model="form.unitId" placeholder="请选择单位" style="width: 500px;" @change="selectUnitt">
<el-option :label="item.name" :value="item.id" v-for="item in units" :key="item.id"></el-option>
</el-select>
<el-button type="primary" plain icon="el-icon-plus" @click="$refs.addUnitRef.show()">添加单位</el-button>
<addUnit ref="addUnitRef" @success="tbShopUnit" />
</el-form-item>
<el-form-item label="商品分类" prop="categoryId">
<el-select v-model="form.categoryId" placeholder="请选择商品分类" style="width: 500px;">
<el-option :label="item.name" :value="item.id" v-for="item in categorys" :key="item.id"></el-option>
</el-select>
<el-button type="primary" plain icon="el-icon-plus"
@click="$refs.addClassifyRef.show()">添加分类</el-button>
<addClassify ref="addClassifyRef" @success="tbShopCategoryGet" />
</el-form-item>
<el-form-item label="商品图片">
<uploadImg ref="uploadImg" :limit="9" @success="uploadSuccess" @remove="uploadRemove" />
<div class="tips">第一张图为商品封面图图片尺寸为750×750(可拖动图片排序)</div>
</el-form-item>
<el-form-item label="套餐商品" v-if="shopTypes[shopTypesActive].typeEnum == 'group'">
<el-table :data="form.groupSnap" border v-if="form.groupSnap.length">
<el-table-column label="标题" prop="title">
<template v-slot="scope">
<el-input v-model="scope.row.title" placeholder="请输入标题:自选小吃"></el-input>
</template>
</el-table-column>
<el-table-column label="商品信息">
<template v-slot="scope">
<div class="shop_list">
<div class="item" v-for="item in scope.row.goods" :key="item.id">
<span class="dot"></span>
<div class="name">
<div class="t">{{ item.name }}</div>
</div>
<i class="del el-icon-delete"></i>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="几选几">
<template v-slot="scope">
<span>{{ scope.row.goods.length }}</span>
<el-input-number v-model="scope.row.number" controls-position="right"
:min="1"></el-input-number>
</template>
</el-table-column>
<el-table-column label="操作" width="160">
<template v-slot="scope">
<el-button type="text" @click="tableAddShop(scope.$index, scope.row.goods)">添加商品</el-button>
<el-button type="text" @click="form.groupSnap.splice(scope.$index, 1)">删除分组</el-button>
</template>
</el-table-column>
</el-table>
<el-button type="text" @click="$refs.shopListRef.show()">添加分组</el-button>
<shopList ref="shopListRef" @success="selectShopRes" />
</el-form-item>
<el-form-item label="规格属性" v-if="shopTypes[shopTypesActive].typeEnum != 'sku'">
<el-table :data="form.skuList" border>
<el-table-column label="售价" prop="salePrice">
<template v-slot="scope">
<el-input-number v-model="scope.row.salePrice" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="会员价" prop="memberPrice">
<template v-slot="scope">
<el-input-number v-model="scope.row.memberPrice"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="成本价" prop="costPrice">
<template v-slot="scope">
<el-input-number v-model="scope.row.costPrice" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="原价" prop="originPrice">
<template v-slot="scope">
<el-input-number v-model="scope.row.originPrice"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="库存数量" prop="stockNumber">
<template v-slot="scope">
<el-input-number v-model="scope.row.stockNumber" :disabled="!!form.id"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="分销金额" prop="firstShared">
<template v-slot="scope">
<el-input-number v-model="scope.row.firstShared"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="商品条码">
<template v-slot="scope">
<el-input v-model="scope.row.barCode" disabled></el-input>
</template>
</el-table-column>
</el-table>
<div class="tips" v-if="form.isShowMall">小程序商城必须设置库存数量大于0</div>
</el-form-item>
<el-form-item label="选择规格" v-if="shopTypes[shopTypesActive].typeEnum == 'sku' && !form.id">
<el-select v-model="form.specId" placeholder="请选择规格" style="width: 500px;" @change="selectSpecHandle">
<el-option :label="item.name" :value="item.id" v-for="item in specList" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="item.name" v-if="selectSpec.length && !isEditor" v-for="item in selectSpec"
:key="item.name">
<el-checkbox-group v-model="item.selectSpecResult" @change="selectSpecResultChange">
<el-checkbox :label="item" v-for="(item, index) in item.value
" :key="index"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item v-if="selectSpecResult && shopTypes[shopTypesActive].typeEnum == 'sku'">
<el-table :data="form.skuList" border>
<el-table-column :label="item.label" :prop="item.value" v-for="(item, index) in specTableHeaders"
:key="index">
</el-table-column>
<el-table-column label="图片" prop="coverImg" width="80">
<template v-slot="scope">
<uploadImg type="text" :limit="1" @success="res => scope.row.coverImg = res[0]"
v-if="!scope.row.coverImg" />
<el-image style="width:30px;height:30px;" :src="scope.row.coverImg" v-else />
</template>
</el-table-column>
<el-table-column label="售价" prop="salePrice">
<template slot="header" slot-scope="scope">
<span>售价</span>
<i class="icon el-icon-edit" @click="batchNumber('salePrice')"></i>
</template>
<template slot-scope="scope">
<el-input-number v-model="scope.row.salePrice" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="会员价" prop="memberPrice">
<template slot="header" slot-scope="scope">
<span>会员价</span>
<i class="icon el-icon-edit" @click="batchNumber('memberPrice')"></i>
</template>
<template slot-scope="scope">
<el-input-number v-model="scope.row.memberPrice"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="成本价" prop="costPrice">
<template slot="header" slot-scope="scope">
<span>成本价</span>
<i class="icon el-icon-edit" @click="batchNumber('costPrice')"></i>
</template>
<template slot-scope="scope">
<el-input-number v-model="scope.row.costPrice" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="原价" prop="originPrice">
<template slot="header" slot-scope="scope">
<span>原价</span>
<i class="icon el-icon-edit" @click="batchNumber('originPrice')"></i>
</template>
<template slot-scope="scope">
<el-input-number v-model="scope.row.originPrice"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="库存数量" prop="stockNumber">
<template slot="header" slot-scope="scope">
<span>库存数量</span>
<i class="icon el-icon-edit" @click="batchNumber('stockNumber')" v-if="!form.id"></i>
</template>
<template slot-scope="scope">
<el-input-number v-model="scope.row.stockNumber" :disabled="!!form.id"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="分销金额" prop="firstShared">
<template slot="header" slot-scope="scope">
<span>分销金额</span>
<i class="icon el-icon-edit" @click="batchNumber('firstShared')"></i>
</template>
<template slot-scope="scope">
<el-input-number v-model="scope.row.firstShared"
controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="商品条码">
<template v-slot="scope">
<el-input v-model="scope.row.barCode" disabled></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template v-slot="scope">
<el-button type="text" @click="form.skuList.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="tips" v-if="form.isShowMall">注小程序商城必须设置库存数量大于0</div>
</el-form-item>
<el-form-item label="上架区域">
<div class="shop_type_box">
<div class="item" :class="{ active: form.isShowCash }" @click="areaChange('isShowCash')">
<div class="s_title">收银台</div>
<div class="active_dot">
<i class="el-icon-check"></i>
</div>
</div>
<div class="item" :class="{ active: form.isShowMall }" @click="areaChange('isShowMall')">
<div class="s_title">小程序商城</div>
<div class="active_dot">
<i class="el-icon-check"></i>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="库存开关">
<el-switch v-model="form.isStock" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips">注:关闭则不计算出入库数据</div>
</el-form-item>
<el-form-item label="标签打印">
<el-switch v-model="form.enableLabel" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips">开启后: 收银完成后会自动打印对应数量的标签数</div>
</el-form-item>
<el-form-item label="打包费">
<el-input-number v-model="form.packFee" controls-position="right" :min="0"></el-input-number>
<div class="tips">单份商品打包费。注:店铺开启外卖模式下该数据才生效</div>
</el-form-item>
<el-form-item label="虚拟销量">
<el-input-number v-model="form.baseSalesNumber" controls-position="right" :min="0"></el-input-number>
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort" controls-position="right" :min="0"></el-input-number>
</el-form-item>
<el-form-item>
<el-button type="primary" v-loading="loading" @click="submitHandle">确定</el-button>
<el-button @click="$router.back()">取消</el-button>
</el-form-item>
</el-form>
<el-dialog title="批量修改" width="400px" :visible.sync="showBatchModal">
<el-form :model="batchNumberForm">
<el-form-item>
<el-input-number v-model="batchNumberForm.batchNumber" :min="0" controls-position="right"
style="width: 100%;"></el-input-number>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showBatchModal = false">取 消</el-button>
<el-button type="primary" @click="batchNumberFormConfirm">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { tbShopUnit, tbShopCategoryGet, tbProductPost, tbProductSpecGet, tbProductGetDetail, tbProductPut } from "@/api/shop";
import addUnit from './components/addUnit'
import addClassify from './components/addClassify'
import shopList from '@/components/shopList'
import uploadImg from '@/components/uploadImg'
import settings from '@/settings'
import dayjs from 'dayjs'
import { RandomNumBoth } from "@/utils";
export default {
components: {
addUnit,
addClassify,
uploadImg,
shopList
},
data() {
return {
shopTypesActive: 0,
shopTypes: settings.typeEnum,
specTableHeaders: [],
specTableBodys: [],
specId: '',
specList: [],
selectSpec: [],
selectSpecResult: false,
defaultSku: {
salePrice: 0,
memberPrice: 0,
costPrice: 0,
originPrice: 0,
stockNumber: 0,
firstShared: 0,
barCode: `${localStorage.getItem('shopId')}${dayjs().valueOf()}`
},
tableAddShopIndex: null,
isEditor: false,
loading: false,
form: {
id: '',
typeEnum: 'normal',
name: '',
unitId: '',
unitName: '',
categoryId: '', // 商品分类id
coverImg: '',
images: [],
shopId: localStorage.getItem('shopId'),
lowPrice: '',
skuList: [],
isShowMall: 1,
isShowCash: 1,
isStock: 0,
packFee: 0,
specId: '',
baseSalesNumber: 0,
sort: 0,
groupSnap: [],
specInfo: [],
selectSpec: [],
specTableHeaders: [],
skuSnap: ''
},
rules: {
typeEnum: [
{
required: true
}
],
name: [
{
required: true,
trigger: 'blur',
message: '请输入商品名称'
}
],
categoryId: [
{
required: true,
trigger: 'change',
message: '请选择商品分类'
}
]
},
units: [],
categorys: [],
// 批量修改规格
showBatchModal: false,
batchNumberKey: '',
batchNumberForm: {
batchNumber: 0
}
}
},
mounted() {
this.tbShopUnit()
this.tbShopCategoryGet()
this.changeTypeEnum(0)
if (this.$route.query.goods_id) {
this.tbProductGetDetail()
}
},
methods: {
// 修改商家区域
areaChange(key) {
if (this.form[key] == 1) {
this.form[key] = 0
} else {
this.form[key] = 1
}
},
selectUnitt(e) {
this.form.unitName = this.units.find(item => item.id == e).name
},
// 批量修改规格
batchNumber(key) {
this.batchNumberKey = key
this.showBatchModal = true
},
// 确认批量修改规格
batchNumberFormConfirm() {
this.form.skuList.map(item => {
item[this.batchNumberKey] = this.batchNumberForm.batchNumber
})
this.showBatchModal = false
this.batchNumberForm.batchNumber = 0
},
// 商品详情
async tbProductGetDetail() {
try {
const res = await tbProductGetDetail(this.$route.query.goods_id)
// 赋值商品类型
this.changeTypeEnum(this.shopTypes.findIndex(item => item.typeEnum == res.typeEnum))
this.specTableHeaders = JSON.parse(res.specTableHeaders)
this.selectSpec = JSON.parse(res.selectSpec)
// 初始化上传图片
this.$refs.uploadImg.fileList = res.images.map(item => {
return {
url: item
}
})
this.form = res
if (res.typeEnum == 'sku') {
let skuList = [...res.skuList]
let specInfo = JSON.parse(res.specInfo)
this.isEditor = true
this.form.skuList = skuList.map((item, index) => {
specInfo[index].id = item.id
return specInfo[index]
})
this.selectSpecResult = true
}
} catch (error) {
console.log(error)
}
},
// 提交
submitHandle() {
this.$refs.formRef.validate(async faild => {
try {
if (faild) {
this.loading = true
this.form.lowPrice = this.form.skuList[0].salePrice
this.form.coverImg = this.form.images[0]
this.form.selectSpec = JSON.stringify(this.selectSpec)
this.form.specTableHeaders = JSON.stringify(this.specTableHeaders)
this.form.specInfo = JSON.stringify(this.form.skuList)
if (this.form.id) {
await tbProductPut(this.form)
} else {
await tbProductPost(this.form)
}
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
this.$router.back()
this.loading = false
}
} catch (error) {
this.loading = false
console.log(error)
}
})
},
// 给分组添加商品
tableAddShop(index, goods) {
this.tableAddShopIndex = index
this.$refs.shopListRef.show([...goods])
},
// 分组选择商品
selectShopRes(res) {
if (this.tableAddShopIndex != null) {
this.form.groupSnap[this.tableAddShopIndex].goods = res;
this.tableAddShopIndex = null
} else {
this.form.groupSnap.push({
title: '',
goods: res,
number: 1
})
}
},
// 切换类型
changeTypeEnum(index) {
if (this.form.id) return
this.shopTypesActive = index
const typeEnum = this.shopTypes[index].typeEnum
this.form.typeEnum = typeEnum
if (typeEnum == 'sku') {
this.tbProductSpecGet()
} else {
this.specId = ''
this.form.specId = ''
this.selectSpec = []
this.selectSpecResult = ''
this.form.skuList = [JSON.parse(JSON.stringify(this.defaultSku))]
}
},
// 上传图片
uploadSuccess(res) {
this.form.images.push(res[0])
},
// 删除突破按
uploadRemove(arr) {
this.form.images = arr
},
// 选择规格属性
selectSpecResultChange() {
this.createSkuHeader()
this.createSkuBody()
},
// 生成多规格表体
createSkuBody() {
let bodys = []
let skuSnap = []
for (let item of this.selectSpec) {
if (item.selectSpecResult.length) {
let arr = []
for (let val of item.selectSpecResult) {
arr.push({
[item.name]: val
})
}
skuSnap.push({
name: item.name,
value: item.selectSpecResult.join(',')
})
bodys.push(arr)
}
}
this.form.skuSnap = JSON.stringify(skuSnap)
let arr = this.cartesian(bodys)
// console.log(arr)
let newarr = []
const m = {
coverImg: '',
...this.defaultSku
}
for (let item of arr) {
if (Array.isArray(item)) {
let obj = {}
let specSnap = []
for (let v of item) {
for (let key in v) {
obj[`${key}`] = v[key]
specSnap.push(v[key])
}
}
newarr.push({
specSnap: specSnap.join(','),
...m,
...obj,
barCode: `${dayjs().valueOf()}${RandomNumBoth(1, 9999)}`
})
} else {
let specSnap = []
for (let key in item) {
specSnap.push(item[key])
}
newarr.push({
specSnap: specSnap.join(','),
...m,
...item,
barCode: `${dayjs().valueOf()}${RandomNumBoth(1, 9999)}`
})
}
}
this.form.skuList = []
this.form.skuList = newarr
},
// 笛卡尔积算法
cartesian(arr) {
if (arr.length < 2) return arr[0] || [];
return [].reduce.call(arr, (col, set) => {
let res = [];
col.forEach(c => {
set.forEach(s => {
let t = [].concat(Array.isArray(c) ? c : [c]);
t.push(s);
res.push(t);
})
});
return res;
});
},
// 生成多规格表头
createSkuHeader() {
let i = 0
const headers = []
for (let item of this.selectSpec) {
if (item.selectSpecResult.length) {
i++
headers.push({
label: item.name,
value: item.name
})
}
}
this.selectSpecResult = i
this.specTableHeaders = headers
},
// 选择规格
selectSpecHandle(e) {
this.isEditor = false
const selectSpec = JSON.parse(JSON.stringify(this.specList.find(item => item.id == e).specList))
for (let item in selectSpec) {
selectSpec[item].selectSpecResult = []
}
this.selectSpec = selectSpec
this.form.skuList = []
},
// 获取规格列表
async tbProductSpecGet() {
try {
const res = await tbProductSpecGet({
shopId: localStorage.getItem('shopId'),
sort: 'id',
page: 0,
size: 100
})
this.specList = res.content
} catch (error) { }
},
// 获取单位
async tbShopUnit() {
try {
const res = await tbShopUnit({
shopId: localStorage.getItem('shopId'),
sort: 'id',
page: 0,
size: 100
})
this.units = res.content
} catch (error) { }
},
// 商品分类列表
async tbShopCategoryGet() {
try {
const res = await tbShopCategoryGet({
shopId: localStorage.getItem('shopId'),
sort: 'id',
page: 0,
size: 100
})
let categorys = []
for (let item of res.content) {
categorys.push({
name: `|----${item.name}`,
id: item.id
})
if (item.childrenList.length) {
for (let val of item.childrenList) {
categorys.push({
name: `|----|----${val.name}`,
id: val.id
})
}
}
}
this.categorys = categorys
} catch (error) {
console.log('商品分类列表', error)
}
}
}
}
</script>
<style scoped lang="scss">
.shop_list {
.item {
display: flex;
align-items: center;
.dot {
$size: 6px;
width: $size;
height: $size;
border-radius: 50%;
background-color: #1890FF;
}
.name {
flex: 1;
margin-left: 10px;
.t {
color: #333;
font-size: 14px;
max-width: 100px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.del {
font-size: 14px;
color: #999;
&:hover {
cursor: pointer;
color: #555;
}
}
}
}
.icon {
font-size: 14px;
color: #1890FF;
margin-left: 10px;
&:hover {
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,140 @@
<template>
<div class="app-container">
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addClassifyRef.show()">添加分类</el-button>
<addClassify ref="addClassifyRef" @success="getTableData" />
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading" row-key="id"
:tree-props="{ children: 'childrenList' }">
<el-table-column label="排序" prop="sort" sortable width="100"></el-table-column>
<el-table-column label="分类名称" prop="name"></el-table-column>
<el-table-column label="分类图片">
<template v-slot="scope">
<el-image :src="scope.row.pic"
style="width: 50px;height: 50px;border-radius: 4px;background-color: #efefef;">
<div class="img_error" slot="error">
<i class="icon el-icon-document-delete"></i>
</div>
</el-image>
</template>
</el-table-column>
<el-table-column label="状态">
<template v-slot="scope">
<el-switch v-model="scope.row.isShow" :active-value="1" :inactive-value="0"
@change="showChange($event, scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="颜色">
<template v-slot="scope">
<div
:style="{ width: '20px', height: '20px', borderRadius: '50%', backgroundColor: scope.row.style || '#efefef' }">
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="300">
<template v-slot="scope">
<el-button type="text" size="mini" round icon="el-icon-plus"
@click="$refs.addClassifyRef.show({ pid: scope.row.id })"
v-if="!scope.row.pid">添加子分类</el-button>
<el-button type="text" size="mini" round icon="el-icon-edit"
@click="$refs.addClassifyRef.show(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="delHandle([scope.row.id])">
<el-button type="text" size="mini" round icon="el-icon-delete" slot="reference">
删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</template>
<script>
import addClassify from './components/addClassify'
import { tbShopCategoryGet, tbShopCategoryDelete, tbShopCategoryPost } from '@/api/shop'
export default {
components: {
addClassify
},
data() {
return {
tableData: {
page: 0,
size: 10,
total: 0,
loading: false,
list: []
}
}
},
mounted() {
this.getTableData()
},
methods: {
// 状态切换
async showChange(e, row) {
try {
await tbShopCategoryPost(row, 'put')
this.getTableData()
} catch (error) {
console.log(error);
}
},
// 添加子分类
addChildGatgory(row) {
},
// 查询table
toQuery() {
this.getTableData()
},// 重置查询
resetHandle() {
this.tableData.page = 0;
this.query.blurry = ''
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1;
this.getTableData()
},
// 删除
async delHandle(ids) {
try {
await tbShopCategoryDelete(ids)
this.$notify({
title: '成功',
message: `删除成功`,
type: 'success'
});
this.getTableData()
} catch (error) {
console.log(error)
}
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbShopCategoryGet({
page: this.tableData.page,
size: this.tableData.size,
sort: 'id,desc',
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error)
}
}
}
}
</script>

View File

@@ -0,0 +1,120 @@
<template>
<el-dialog title="添加分类" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
<!-- <el-form-item label="层级">
<el-select v-model="form.index">
<el-option label="顶级" :value="1"></el-option>
<el-option label="饮品" :value="2"></el-option>
<el-option label="烤串" :value="3"></el-option>
</el-select>
</el-form-item> -->
<el-form-item label="分类名称" prop="name">
<el-input v-model="form.name" placeholder="请输入分类名称"></el-input>
</el-form-item>
<el-form-item label="分类图片">
<uploadImg ref="uploadImg" @success="res => form.pic = res[0]" @remove="form.pic = ''" />
</el-form-item>
<el-form-item label="颜色标识">
<el-color-picker v-model="form.style"></el-color-picker>
<div style="color: #999;">
标识色用在无图模式时的商品点单按钮显示可以更有效的从视觉.上进行商品分组
</div>
</el-form-item>
<el-form-item label="开关">
<el-switch v-model="form.isShow" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort" controls-position="right" :min="0"></el-input-number>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
</template>
<script>
import uploadImg from '@/components/uploadImg'
import { tbShopCategoryPost } from '@/api/shop'
export default {
components: {
uploadImg
},
data() {
return {
dialogVisible: false,
form: {
id: '',
pid: '',
isShow: 1,
name: '',
sort: '',
style: '#000000',
pic: ''
},
resetForm: '',
rules: {
name: [
{
required: true,
message: ' ',
trigger: 'blur'
}
]
}
}
},
mounted() {
this.resetForm = { ...this.form }
},
methods: {
onSubmitHandle() {
console.log(this.form)
this.$refs.form.validate(async valid => {
if (valid) {
try {
this.form.shopId = localStorage.getItem('shopId')
let res = await tbShopCategoryPost(this.form, this.form.id ? 'put' : 'post')
this.$emit('success', res)
this.close()
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
} catch (error) {
console.log(error)
}
}
})
},
show(obj) {
// console.log(obj)
this.dialogVisible = true
if (obj.pid) {
this.form.pid = obj.pid
}
if (obj && obj.id) {
this.form = obj
if (obj.pic) {
setTimeout(() => {
this.$refs.uploadImg.fileList = [
{
url: obj.pic
}
]
}, 100)
}
}
},
close() {
this.dialogVisible = false
},
reset() {
this.form = { ...this.resetForm }
this.$refs.uploadImg.clearFiles()
}
}
}
</script>

View File

@@ -0,0 +1,222 @@
<template>
<div>
<el-dialog title="添加分组" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-form-item label="分组名称" prop="name">
<el-input v-model="form.name" placeholder="请输入分组名称"></el-input>
</el-form-item>
<el-form-item label="选择商品">
<div>
<el-button type="primary" icon="el-icon-plus" @click="$refs.shopListRef.show([...productIds])">
添加商品
</el-button>
</div>
<div class="shop_list">
<div class="item_wrap" v-for="(item, index) in productIds" :key="item.id"
@click="productIds.splice(index, 1)">
<div class="item" :data-index="index + 1">
<el-image :src="item.coverImg" style="width: 100%;height: 100%;"></el-image>
</div>
<div class="name">{{ item.name }}</div>
</div>
</div>
</el-form-item>
<el-form-item label="分组状态">
<el-radio-group v-model="form.isShow">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="分组排序">
<el-input-number v-model="form.sort" controls-position="right" :min="0"></el-input-number>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="loading" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
<shopList ref="shopListRef" @success="slectShop" />
</div>
</template>
<script>
import { tbProductGroupPost, tbProductGroupPut, productListGet } from '@/api/shop'
import shopList from '@/components/shopList'
export default {
components: {
shopList
},
data() {
return {
dialogVisible: false,
loading: false,
form: {
id: '',
name: '',
isShow: 1,
sort: 0,
productIds: [],
shopId: localStorage.getItem('shopId')
},
rules: {
name: [
{
required: true,
message: ' ',
trigger: 'blur'
}
]
},
productIds: []
}
},
methods: {
slectShop(res) {
if (this.productIds.length) {
res.map(async item => {
if (!await this.checkShop(item.id)) {
this.productIds.push({ ...item })
}
})
} else {
this.productIds = res
}
},
// 判断是否存在重复商品
checkShop(id) {
let falg = false
this.productIds.map(item => {
if (item.id == id) {
falg = true
}
})
return falg
},
onSubmitHandle() {
this.$refs.form.validate(async valid => {
if (valid) {
this.loading = true
try {
this.form.productIds = this.productIds.map(item => item.id);
let res = null
if (!this.form.id) {
await tbProductGroupPost(this.form)
} else {
await tbProductGroupPut(this.form)
}
this.loading = false
this.$emit('success', res)
this.close()
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
} catch (error) {
console.log(error)
}
}
})
},
async getProduts() {
try {
const res = await productListGet(this.form.id)
this.productIds = res
} catch (error) {
console.log(error)
}
},
show(obj) {
if (obj && obj.id) {
this.form.id = obj.id
this.form.isShow = obj.isShow
this.form.name = obj.name
this.form.sort = obj.sort
this.form.productIds = obj.productIds
this.getProduts()
}
this.dialogVisible = true
},
close() {
this.dialogVisible = false
},
reset() {
this.form.isShow = 1
this.form.name = ''
this.form.sort = 0
this.form.productIds = []
this.productIds = []
}
}
}
</script>
<style scoped lang="scss">
.shop_list {
display: flex;
flex-wrap: wrap;
.item_wrap {
$size: 80px;
.item {
$radius: 4px;
width: $size;
height: $size;
border-radius: $radius;
overflow: hidden;
position: relative;
margin-right: 10px;
margin-top: 10px;
&:hover {
cursor: pointer;
}
&::after {
content: attr(data-index);
font-size: 12px;
height: 20px;
display: flex;
padding: 0 10px;
border-radius: 0 0 $radius 0;
align-items: center;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
color: #fff;
position: absolute;
top: 0;
left: 0;
z-index: 10;
}
&::before {
content: '删除';
font-size: 12px;
width: 100%;
height: 20px;
display: flex;
padding: 0 10px;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
color: #fff;
position: absolute;
bottom: 0;
left: 0;
z-index: 10;
transition: all .1s ease-in-out;
}
}
.name {
width: $size;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
</style>

View File

@@ -0,0 +1,203 @@
<template>
<el-dialog title="添加模板" width="840px" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="80px" label-position="left">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="模板名称,如:衣服"></el-input>
</el-form-item>
<el-form-item :label="item.name" v-for="(item, index) in form.specList" :key="index">
<div class="tag_wrap">
<el-tag v-for="(val, i) in item.value" :key="i" closable size="medium" :disable-transitions="true"
@close="handleClose(index, i)">
{{ val }}
</el-tag>
<el-input class="input-new-tag" v-show="item.inputVisible" v-model="item.inputValue" ref="saveTagInput"
size="small" placeholder="请输入规格值,回车添加" @keyup.enter.native="handleInputConfirm(index)"
@blur="handleInputConfirm(index)">
</el-input>
<el-button v-show="!item.inputVisible" size="mini" icon="el-icon-plus" plain @click="showInput(index)">
添加
</el-button>
<el-button size="mini" icon="el-icon-delete" plain @click="deleteTag(index)">删除</el-button>
</div>
</el-form-item>
</el-form>
<el-form ref="skuForm" :model="skuForm" :rules="skuRules" label-width="80px" label-position="left">
<el-row :gutter="20" v-if="showSkuForm">
<el-col :span="10">
<el-form-item label="规格" prop="skuValidate1">
<el-input v-model="skuForm.label" placeholder="规格,如:尺码"></el-input>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="规格值" prop="skuValidate2">
<el-input v-model="skuForm.value" placeholder="规格值S、M"></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-button type="primary" @click="addSkuSubmit">添加</el-button>
<el-button @click="showSkuForm = false">取消</el-button>
</el-col>
</el-row>
<el-form-item v-if="!showSkuForm">
<el-button type="primary" icon="el-icon-plus" @click="showSkuForm = true">添加规格</el-button>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
</template>
<script>
import { tbProductSpecPost, tbProductSpecPut } from '@/api/shop'
export default {
data() {
const validateSku1 = (rule, value, callback) => {
if (!this.skuForm.label) {
callback(new Error(' '))
} else {
callback()
}
}
const validateSku2 = (rule, value, callback) => {
if (!this.skuForm.value) {
callback(new Error(' '))
} else {
callback()
}
}
return {
dialogVisible: false,
showSkuForm: true,
skuForm: {
label: '',
value: ''
},
skuRules: {
skuValidate1: {
required: true,
validator: validateSku1,
trigger: 'blur'
},
skuValidate2: {
required: true,
validator: validateSku2,
trigger: 'blur'
}
},
form: {
id: '',
name: '',
shopId: localStorage.getItem('shopId'),
specList: []
},
rules: {
name: [
{
required: true,
message: ' ',
trigger: 'blur'
}
]
}
}
},
methods: {
onSubmitHandle() {
console.log(this.form)
this.$refs.form.validate(async valid => {
if (valid) {
try {
let res = null
if (!this.form.id) {
res = await tbProductSpecPost(this.form)
} else {
res = await tbProductSpecPut(this.form)
}
this.$emit('success', res)
this.close()
this.$notify({
title: '成功',
message: `${this.form.pid ? '编辑' : '添加'}成功`,
type: 'success'
});
} catch (error) {
console.log(error)
}
}
})
},
show(obj) {
this.dialogVisible = true
if (obj && obj.id) {
const newObj = JSON.parse(JSON.stringify(obj));
this.form.id = newObj.id
this.form.name = newObj.name
const specList = newObj.specList
for (let item of specList) {
item.inputVisible = false
item.inputValue = ''
}
this.form.specList = specList
}
},
close() {
this.dialogVisible = false
},
reset() {
this.form.id = ''
this.form.name = ''
this.form.specList = []
},
// sku from
addSkuSubmit() {
this.$refs.skuForm.validate(async valid => {
if (valid) {
this.form.specList.push({
name: this.skuForm.label,
value: [this.skuForm.value],
inputVisible: false,
inputValue: ''
})
this.skuForm.label = ''
this.skuForm.value = ''
this.showSkuForm = false
}
})
},
handleClose(index, i) {
this.form.specList[index].value.splice(i, 1);
},
showInput(index) {
this.form.specList[index].inputVisible = true;
},
handleInputConfirm(index) {
let inputValue = this.form.specList[index].inputValue;
if (inputValue) {
this.form.specList[index].value.push(inputValue);
}
this.form.specList[index].inputVisible = false;
this.form.specList[index].inputValue = '';
},
// 删除已添加的规格
deleteTag(index) {
this.form.specList.splice(index, 1);
}
}
}
</script>
<style scoped lang="scss">
.tag_wrap {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.input-new-tag {
width: 180px;
margin-left: 10px;
vertical-align: bottom;
}
</style>

View File

@@ -0,0 +1,86 @@
<template>
<el-dialog title="添加单位" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="120" label-position="left">
<el-form-item label="单位名称" prop="name">
<el-input v-model="form.name" placeholder="单位名称"></el-input>
</el-form-item>
<el-form-item label="排序">
<el-input v-model="form.sort"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="formLoading" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
</template>
<script>
import { tbShopUnitPost, tbShopUnitPut } from '@/api/shop'
export default {
data() {
return {
dialogVisible: false,
formLoading: false,
form: {
id: '',
name: '',
sort: 0,
shopId: localStorage.getItem('shopId')
},
rules: {
name: [
{
required: true,
message: ' ',
trigger: 'blur'
}
]
}
}
},
methods: {
onSubmitHandle() {
this.$refs.form.validate(async valid => {
if (valid) {
try {
this.formLoading = true
let res = null
if (!this.form.id) {
res = await tbShopUnitPost(this.form)
} else {
res = await tbShopUnitPut(this.form)
}
this.close()
this.formLoading = false
this.$emit('success', res)
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
} catch (error) {
this.formLoading = false
}
}
})
},
show(obj) {
this.dialogVisible = true
if (obj && obj.id) {
// 编辑
this.form.name = obj.name
this.form.id = obj.id
}
},
close() {
this.dialogVisible = false
},
reset() {
this.form.id = ''
this.form.name = ''
this.form.sort = 0
}
}
}
</script>

View File

@@ -0,0 +1,109 @@
<template>
<div class="app-container">
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addGroupRef.show()">
添加分组
</el-button>
<addGroup ref="addGroupRef" @success="resetHandle" />
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="排序" sortable prop="sort"></el-table-column>
<el-table-column label="分组名称" prop="name"></el-table-column>
<el-table-column label="状态">
<template v-slot="scope">
<el-switch v-model="scope.row.isShow" :active-value="1" :inactive-value="0"
@change="showChange($event, scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template v-slot="scope">
<el-button type="text" size="mini" round icon="el-icon-edit"
@click="$refs.addGroupRef.show(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="delHandle([scope.row.id])">
<el-button type="text" size="mini" round icon="el-icon-delete" slot="reference">
删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</template>
<script>
import addGroup from '../components/addGroup'
import { tbProductGroupGet, tbProductGroupDelete, tbProductGroupPut } from '@/api/shop'
export default {
components: {
addGroup
},
data() {
return {
tableData: {
page: 0,
size: 10,
total: 0,
loading: false,
list: []
}
}
},
mounted() {
this.getTableData()
},
methods: {
// 状态切换
async showChange(e, row) {
try {
await tbProductGroupPut(row)
this.getTableData()
} catch (error) {
console.log(error);
}
},
// 重置查询
resetHandle() {
this.tableData.page = 0;
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 删除
async delHandle(ids) {
try {
await tbProductGroupDelete(ids)
this.$notify({
title: '成功',
message: `删除成功`,
type: 'success'
});
this.getTableData()
} catch (error) {
console.log(error)
}
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbProductGroupGet({
page: this.tableData.page,
size: this.tableData.size,
sort: 'id',
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) { }
}
}
}
</script>

216
src/views/product/index.vue Normal file
View File

@@ -0,0 +1,216 @@
<template>
<div class="app-container">
<div class="head-container">
<el-row :gutter="20">
<el-col :span="3">
<el-input v-model="query.name" size="small" clearable placeholder="请输入商品名称" style="width: 100%;"
class="filter-item" @keyup.enter.native="getTableData" />
</el-col>
<el-col :span="3">
<el-select v-model="query.categoryId" placeholder="请选择商品分类" style="width: 100%;">
<el-option :label="item.name" :value="item.id" v-for="item in categorys" :key="item.id" />
</el-select>
</el-col>
<el-col :span="3">
<el-select v-model="query.typeEnum" placeholder="请选择商品规格" style="width: 100%;">
<el-option :label="item.label" :value="item.typeEnum" v-for="item in typeEnums" :key="item.typeEnum" />
</el-select>
</el-col>
<el-col :span="6">
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-col>
</el-row>
</div>
<div class="head-container">
<el-row>
<el-col>
<router-link :to="{ name: 'add_shop' }">
<el-button type="primary" icon="el-icon-plus">添加商品</el-button>
</router-link>
</el-col>
</el-row>
</div>
<div class="head-container">
<el-table :data="tableData.data" v-loading="tableData.loading">
<el-table-column label="商品信息">
<template v-slot="scope">
<div class="shop_info">
<el-image :src="scope.row.coverImg"
style="width: 50px;height: 50px;border-radius: 4px;background-color: #efefef;">
<div class="img_error" slot="error">
<i class="icon el-icon-document-delete"></i>
</div>
</el-image>
<div class="info">
<span>{{ scope.row.name }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="售价">
<template v-slot="scope">
<span>{{ scope.row.lowPrice }}</span>
</template>
</el-table-column>
<el-table-column label="销量/库存">
<template v-slot="scope">
<span>{{ scope.row.realSalesNumber }}/{{ scope.row.stockNumber }}</span>
</template>
</el-table-column>
<el-table-column label="上架区域">
<template v-slot="scope">
<div v-if="scope.row.isShowCash">收银端</div>
<div v-if="scope.row.isShowMall">小程序</div>
<div v-if="!scope.row.isShowCash && !scope.row.isShowMall">未上架</div>
</template>
</el-table-column>
<el-table-column label="排序" prop="sort" sortable />
<el-table-column label="更新时间" prop="createdAt">
<template v-slot="scope">
{{ dayjs(scope.row.createdAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="设为热门" prop="createdAt">
<template v-slot="scope">
<el-switch v-model="scope.row.isHot" :active-value="1" :inactive-value="0"
@change="changeHot($event, scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template v-slot="scope">
<router-link :to="{ path: '/product/add_shop', query: { goods_id: scope.row.id } }">
<el-button type="text" icon="el-icon-edit">编辑</el-button>
</router-link>
<el-popconfirm title="确定删除吗?" @confirm="delTableHandle([scope.row.id])">
<el-button type="text" icon="el-icon-delete" style="margin-left: 20px !important;"
slot="reference">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</template>
<script>
import dayjs from 'dayjs'
import settings from '@/settings'
import { tbProduct, tbShopCategoryGet, tbProductDelete, tbProductIsHot } from '@/api/shop'
export default {
data() {
return {
dayjs,
query: {
name: '',
categoryId: '',
typeEnum: ''
},
categorys: [],
typeEnums: settings.typeEnum,
tableData: {
data: [],
page: 0,
size: 10,
loading: false,
total: 0
}
}
},
mounted() {
this.getTableData()
this.tbShopCategoryGet()
},
methods: {
// 设置热门
async changeHot(e, row) {
console.log(row)
try {
this.tableData.loading = true
await tbProductIsHot({
shopId: localStorage.getItem('shopId'),
id: row.id
})
this.getTableData()
} catch (error) {
console.log(error);
}
},
// 重置查询
resetHandle() {
this.query.name = ''
this.query.categoryId = ''
this.query.typeEnum = ''
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbProduct({
page: this.tableData.page,
size: this.tableData.size,
name: this.query.name,
categoryId: this.query.categoryId,
typeEnum: this.query.typeEnum,
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.data = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error)
}
},
// 获取商品分类列表
async tbShopCategoryGet() {
try {
const res = await tbShopCategoryGet({
shopId: localStorage.getItem('shopId'),
page: 0,
size: 100,
sort: 'id'
})
this.categorys = res.content
} catch (error) {
console.log(error)
}
},
// 删除商品
async delTableHandle(ids) {
try {
await tbProductDelete(ids)
this.getTableData()
} catch (error) {
console.log(error)
}
},
}
}
</script>
<style scoped lang="scss">
.shop_info {
display: flex;
.info {
flex: 1;
padding-left: 8px;
display: flex;
flex-direction: column;
.tag_wrap {
display: flex;
}
}
}
</style>

View File

@@ -0,0 +1,118 @@
<template>
<div class="app-container">
<div class="head-container">
<el-row :gutter="20">
<el-col :span="6">
<el-input v-model="query.blurry" size="small" clearable placeholder="请输入模板名称" style="width: 100%;"
class="filter-item" @keyup.enter.native="getTableData" />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-col>
</el-row>
</div>
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addSpecificationRef.show()">添加规格</el-button>
<addSpecification ref="addSpecificationRef" @success="getTableData" />
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="模板名称" prop="name"></el-table-column>
<el-table-column label="规格详情">
<template v-slot="scope">
<el-row v-for="(item, index) in scope.row.specList" :key="index">
<span>{{ item.name }}</span>
<span>{{ item.value.join('、') }}</span>
</el-row>
</template>
</el-table-column>
<el-table-column label="排序" sortable></el-table-column>
<el-table-column label="操作" width="200">
<template v-slot="scope">
<el-button type="text" size="mini" round icon="el-icon-edit"
@click="$refs.addSpecificationRef.show(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="delHandle([scope.row.id])">
<el-button type="text" size="mini" round icon="el-icon-delete" slot="reference">
删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</template>
<script>
import addSpecification from './components/addSpecification'
import { tbProductSpecGet, tbProductSpecDelete } from '@/api/shop'
export default {
components: {
addSpecification
},
data() {
return {
query: {
blurry: '',
},
tableData: {
page: 0,
size: 10,
total: 0,
loading: false,
list: []
}
}
},
mounted() {
this.getTableData()
},
methods: {
// 重置查询
resetHandle() {
this.query.blurry = ''
this.tableData.page = 0;
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 删除
async delHandle(ids) {
try {
const res = await tbProductSpecDelete(ids)
this.$notify({
title: '成功',
message: `删除成功`,
type: 'success'
});
this.getTableData()
} catch (error) {
console.log(error)
}
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbProductSpecGet({
page: this.tableData.page,
size: this.tableData.size,
sort: 'id',
shopId: localStorage.getItem('shopId')
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) { }
}
}
}
</script>

112
src/views/product/unit.vue Normal file
View File

@@ -0,0 +1,112 @@
<template>
<div class="app-container">
<div class="head-container">
<el-row :gutter="20">
<el-col :span="6">
<el-input v-model="query.blurry" size="small" clearable placeholder="请输入单位名称" style="width: 100%;"
class="filter-item" @keyup.enter.native="toQuery" />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="toQuery">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-col>
</el-row>
</div>
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addUnitRef.show()">添加单位</el-button>
<addUnit ref="addUnitRef" @success="getTableData()" />
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="单位名称" prop="name"></el-table-column>
<el-table-column label="排序" prop="id" sortable></el-table-column>
<el-table-column label="操作" width="200">
<template v-slot="scope">
<el-button type="text" size="mini" round icon="el-icon-edit"
@click="$refs.addUnitRef.show(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="delHandle([scope.row.id])">
<el-button type="text" size="mini" round icon="el-icon-delete" slot="reference">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</template>
<script>
import { tbShopCurrencyGet, tbShopUnitDelete } from '@/api/shop'
import addUnit from './components/addUnit'
export default {
components: {
addUnit
},
data() {
return {
query: {
blurry: ''
},
tableData: {
page: 0,
size: 10,
total: 0,
loading: false,
list: []
}
}
},
mounted() {
this.getTableData()
},
methods: {
// 查询table
toQuery() {
this.getTableData()
},// 重置查询
resetHandle() {
this.tableData.page = 0;
this.query.blurry = ''
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1;
this.getTableData()
},
// 删除
async delHandle(ids) {
try {
await tbShopUnitDelete(ids)
this.$notify({
title: '成功',
message: `删除成功`,
type: 'success'
});
this.getTableData()
} catch (error) {
console.log(error)
}
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbShopCurrencyGet({
page: this.tableData.page,
size: this.tableData.size,
sort: 'id',
shopId: localStorage.getItem('shopId'),
name: this.query.blurry
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) { }
}
}
}
</script>

View File

@@ -0,0 +1,80 @@
<template>
<el-dialog title="生产激活码" :visible.sync="dialogVisible" width="500px" @close="reset">
<el-form :model="form" :rules="rules" label-width="100px" label-position="left">
<el-form-item label="激活时长" prop="periodYear">
<el-input-number v-model="form.periodYear" controls-position="right" :min="1" :step="1"
step-strictly></el-input-number>
</el-form-item>
<el-form-item label="生产数量" prop="number">
<el-input-number v-model="form.number" controls-position="right" :min="1" :step="1"
step-strictly></el-input-number>
</el-form-item>
<!-- <el-form-item label="所属代理" prop="agent">
<el-input v-model="form.agent" placeholder="请输入完整的代理账号查找"></el-input>
</el-form-item> -->
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" v-loading="loading" @click="tbMerchantRegisterPost">生产激活码</el-button>
</div>
</el-dialog>
</template>
<script>
import { tbMerchantRegisterPost } from '@/api/shop.js'
export default {
data() {
return {
dialogVisible: false,
loading: false,
form: {
periodYear: 1,
number: 1,
agent: ''
},
rules: {
periodYear: [{
required: true,
message: ' ',
trigger: 'blur'
}],
number: [{
required: true,
message: ' ',
trigger: 'blur'
}]
}
}
},
methods: {
async tbMerchantRegisterPost() {
try {
this.loading = true
const res = await tbMerchantRegisterPost(this.form)
this.$emit('success', res)
this.close()
this.$notify({
title: '成功',
message: `添加成功`,
type: 'success'
});
this.loading = false
} catch (error) {
this.loading = false
console.log(error)
}
},
show() {
this.dialogVisible = true
},
close() {
this.dialogVisible = false
},
reset() {
this.form.periodYear = 1
this.form.number = 1
this.form.agent = ''
}
}
}
</script>

View File

@@ -0,0 +1,160 @@
<template>
<div class="app-container">
<div class="head-container">
<div class="filter_wrap">
<!-- <el-input v-model="query.name" size="small" clearable placeholder="请输入完整的代理商账号查找" style="width: 250px"
@keyup.enter.native="getTableData" /> -->
<el-select v-model="query.type" placeholder="请选择类型" style="width: 200px">
<el-option label="快餐版" value="munchies" />
<el-option label="餐饮版" value="restaurant" />
</el-select>
<el-select v-model="query.status" placeholder="请选择状态" style="width: 200px">
<el-option label="待激活" :value="0" />
<el-option label="已使用" :value="1" />
</el-select>
<el-date-picker v-model="query.createdAt" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss" style="width: 400px" @change="getTableData">
</el-date-picker>
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</div>
</div>
<div class="head-container">
<div class="filter_wrap">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addActivationCode.show()">添加激活码</el-button>
<el-button icon="el-icon-download">导出Excel</el-button>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="激活码" prop="registerCode" width="500px">
<template v-slot="scope">
<el-tooltip content="点击复制">
<el-tag type="success" @click="copyHandle(scope.row.registerCode)">
<i class="el-icon-paperclip"></i>
{{ scope.row.registerCode }}
</el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="商户名称" prop="name"></el-table-column>
<el-table-column label="联系电话" prop="telephone"></el-table-column>
<el-table-column label="版本类型" prop="type">
<template v-slot="scope">
<span v-if="scope.row.type == 'munchies'">快餐版</span>
<span v-if="scope.row.type == 'restaurant'">餐饮版</span>
</template>
</el-table-column>
<el-table-column label="激活时长" prop="periodYear"></el-table-column>
<el-table-column label="状态" prop="status">
<template v-slot="scope">
<el-tag type="info" v-if="scope.row.status == 0">待激活</el-tag>
<el-tag type="success" v-if="scope.row.status == 1">已使用</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createdAt">
<template v-slot="scope">
{{ scope.row.createdAt && dayjs(scope.row.createdAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
<addActivationCode ref="addActivationCode" @success="getTableData" />
</div>
</template>
<script>
import dayjs from 'dayjs'
import { tbMerchantRegisterList } from '@/api/shop.js'
import addActivationCode from './components/addActivationCode'
import VueClipboard from 'vue-clipboard2'
export default {
components: { addActivationCode },
data() {
return {
dayjs,
query: {
name: '',
type: '',
status: '',
createdAt: []
},
status: [
{
type: 1,
label: '开启'
},
{
type: 0,
label: '关闭'
}
],
tableData: {
list: [],
page: 0,
size: 10,
loading: false,
total: 0
}
}
},
mounted() {
this.getTableData()
},
methods: {
copyHandle(text) {
this.$copyText(text).then((e) => {
this.$notify({
title: '成功',
message: `复制成功`,
type: 'success'
});
})
},
// 重置查询
resetHandle() {
this.query.name = ''
this.query.account = ''
this.query.status = ''
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
async getTableData() {
this.tableData.loading = true
try {
const res = await tbMerchantRegisterList({
page: this.tableData.page,
size: this.tableData.size,
type: this.query.type,
status: this.query.status,
createdAt: this.query.createdAt
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error)
}
}
}
}
</script>
<style scoped lang="scss">
.shop_info {
display: flex;
.info {
flex: 1;
padding-left: 4px;
}
}
</style>

View File

@@ -0,0 +1,351 @@
<template>
<el-dialog :title="form.id ? '编辑店铺' : '添加店铺'" :visible.sync="dialogVisible" @close="reset">
<div style="height: 50vh;overflow-y: auto;">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-form-item label="店铺名称" prop="shopName">
<el-input v-model="form.shopName" placeholder="请输入门店名称"></el-input>
</el-form-item>
<el-form-item label="门店logo" prop="logo">
<el-image :src="form.logo || require('@/assets/images/upload.png')" fit="contain"
style="width: 80px;height: 80px;" @click="showUpload = true; uploadIndex = 1"></el-image>
</el-form-item>
<el-form-item label="门店照片">
<el-image :src="form.coverImg || require('@/assets/images/upload.png')" fit="contain"
style="width: 80px;height: 80px;" @click="showUpload = true; uploadIndex = 2"></el-image>
</el-form-item>
<el-form-item label="店铺类型">
<el-radio-group v-model="form.registerType">
<el-radio-button label="munchies">快餐版</el-radio-button>
<el-radio-button label="restaurant">餐饮版</el-radio-button>
</el-radio-group>
<div class="tips">请谨慎修改</div>
</el-form-item>
<el-form-item label="试用/正式">
<el-radio-group v-model="form.profiles">
<el-radio-button label="probation">试用</el-radio-button>
<el-radio-button label="release">正式</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="激活码">
<el-input v-model="form.registerCode" placeholder="请输入激活码"></el-input>
<div class="tips">输入有效激活码表示添加的同时直接激活该店铺</div>
</el-form-item>
<el-form-item label="登录账号" prop="account">
<el-input v-model="form.account" placeholder="请输入登录账号"></el-input>
</el-form-item>
<el-form-item label="登录密码" prop="password">
<el-input type="password" show-password v-model="form.password" placeholder="请输入登录密码"></el-input>
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="form.phone" placeholder="请输入联系电话"></el-input>
</el-form-item>
<el-form-item label="设备数量">
<el-input-number v-model="form.supportDeviceNumber" controls-position="right" :min="1" :step="1"
step-strictly></el-input-number>
</el-form-item>
<!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
:min="0"></el-input-number>
</el-form-item> -->
<el-form-item label="店铺经度">
<el-row>
<el-col :span="4">
<el-input v-model="form.lng" placeholder="经度"></el-input>
</el-col>
<el-col :span="4">
<el-input v-model="form.lat" placeholder="纬度" style="margin-left: 10px;"></el-input>
</el-col>
<el-col :span="4">
<el-button type="primary" plain icon="el-icon-place" style="margin-left: 20px;"
@click="showLocation = true">选择坐标</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="店铺详细地址">
<el-input type="textarea" v-model="form.address" placeholder="请输入门店详细地址"></el-input>
</el-form-item>
<el-form-item label="店铺简介">
<el-input type="textarea" v-model="form.detail" placeholder="请输入店铺简介"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio :label="1">开启</el-radio>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<el-dialog title="选择地址" :visible.sync="showLocation" :modal="false" :modal-append-to-body="false">
<div class="map_box">
<div class="map">
<el-amap :center="amapOptions.center">
<el-amap-marker :position="amapOptions.center"></el-amap-marker>
</el-amap>
</div>
<div class="search_box">
<el-amap-search-box :search-option="searchOption"
:on-search-result="onSearchResult"></el-amap-search-box>
</div>
<div class="search_wrap">
<div class="item" v-for="item in locationSearchList" :key="item.id">
<div class="left">
<div class="name">{{ item.name }}-{{ item.address }}</div>
<div class="location">
经纬度{{ item.lng }},{{ item.lat }}
</div>
</div>
<div class="btn">
<el-button type="primary" @click="selectLocationHandle(item)">
选择
</el-button>
</div>
</div>
</div>
</div>
</el-dialog>
<el-dialog :visible.sync="showUpload" :close-on-click-modal="false" append-to-body width="500px"
@close="showUpload = false">
<el-upload :before-remove="handleBeforeRemove" :on-success="handleSuccess" :on-error="handleError"
:file-list="fileList" :headers="headers" :action="qiNiuUploadApi" class="upload-demo" multiple>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" style="display: block;" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</el-button>
<el-button @click="uploadClose">取消</el-button>
</div>
</el-dialog>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="submitHandle" :loading="formLoading">
<span v-if="!formLoading">保存</span>
<span v-else>保存中...</span>
</el-button>
</div>
</el-dialog>
</template>
<script>
import { getToken } from '@/utils/auth'
import { mapGetters } from 'vuex'
import crudQiNiu from '@/api/tools/qiniu'
import { tbShopInfoPost } from '@/api/shop'
export default {
computed: {
...mapGetters([
'qiNiuUploadApi'
])
},
data() {
const validateLogo = (rule, value, callback) => {
if (!this.form.logo) {
callback(new Error('请上传门店logo'))
} else {
callback()
}
}
return {
dialogVisible: false,
showLocation: false,
showUpload: false,
uploadIndex: 1,
startTime: '',
endTime: '',
formLoading: false,
form: {
id: '',
shopName: '',
registerType: 'restaurant',
profiles: 'release',
registerCode: '',
account: '',
password: '',
phone: '',
supportDeviceNumber: '',
lat: '',
lng: '',
address: '',
detail: '',
status: 1,
logo: '',
coverImg: ''
},
resetForm: '',
rules: {
shopName: [
{
required: true,
message: ' ',
trigger: 'blur'
}
],
logo: [
{
required: true,
validator: validateLogo,
trigger: 'change'
}
],
account: [
{
required: true,
message: ' ',
trigger: 'change'
}
],
password: [
{
required: true,
message: ' ',
trigger: 'change'
}
]
},
fileList: [],
files: [],
headers: {
'Authorization': getToken()
},
searchOption: {
city: '西安',
citylimit: false
},
locationSearchList: [],
amapOptions: {
center: [108.946465, 34.347984],
position: []
}
}
},
mounted() {
this.resetForm = { ...this.form }
},
methods: {
onSearchResult(res) {
this.locationSearchList = res
this.amapOptions.center = [res[0].lng, res[0].lat]
},
// 确认地址选择
selectLocationHandle(item) {
this.form.lng = item.lng
this.form.lat = item.lat
this.showLocation = false
},
// 保存
submitHandle() {
this.$refs.form.validate(async (valid) => {
if (valid) {
this.formLoading = true
try {
await tbShopInfoPost(this.form, this.form.id ? 'put' : 'post')
this.$emit('success')
this.formLoading = false
this.$notify({
title: '成功',
message: `${this.form.id ? '编辑' : '添加'}成功`,
type: 'success'
});
this.close()
} catch (error) {
this.formLoading = false
console.log(error)
}
}
})
},
handleSuccess(response, file, fileList) {
// const uid = file.uid
// const id = response.id
// this.files.push({ uid, id })
console.log('上传成功', response)
this.files = response.data
},
handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) {
if (this.files[i].uid === file.uid) {
crudQiNiu.del([this.files[i].id]).then(res => { })
return true
}
}
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
// 监听上传失败
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR)
},
// 刷新列表数据
doSubmit() {
this.fileList = []
this.showUpload = false
switch (this.uploadIndex) {
case 1:
this.form.logo = this.files[0]
break;
case 2:
this.form.coverImg = this.files[0]
break;
default:
break;
}
},
show(obj) {
this.dialogVisible = true
if (obj && obj.id) {
this.form = { ...obj }
}
},
close() {
this.dialogVisible = false
},
uploadClose() {
this.showUpload = false
},
reset() {
this.form = { ...this.resetForm }
}
}
}
</script>
<style scoped lang="scss">
.map_box {
width: 100%;
position: relative;
.map {
height: 300px;
}
.search_box {
position: absolute;
top: 10px;
left: 10px;
}
.search_wrap {
padding: 6px 0;
.item {
display: flex;
padding: 12px 0;
.left {
flex: 1;
display: flex;
flex-direction: column;
padding-right: 20px;
.location {
color: #999;
padding-top: 4px;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<el-dialog :visible.sync="dialogVisible" :show-close="false" @close="reset">
<el-tabs v-model="activeName">
<el-tab-pane label="聚合支付" name="pay">
<el-form ref="form" :model="form" label-width="120px" label-position="left">
<el-form-item label="商户号">
<el-input v-model="form.appId" placeholder="请输入商户号"></el-input>
</el-form-item>
<el-form-item label="商户密钥">
<el-input type="textarea" v-model="form.appToken" placeholder="请输入商户密钥"></el-input>
</el-form-item>
<el-form-item label="支付密码">
<el-input v-model="form.payPassword" placeholder="请输入支付密码"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="-1">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="submitHandle" :loading="formLoading">
<span v-if="!formLoading">保存</span>
<span v-else>保存中...</span>
</el-button>
</div>
</el-dialog>
</template>
<script>
import { tbMerchantThirdApply, tbMerchantThirdApplyPut } from '@/api/shop'
export default ({
data() {
return {
dialogVisible: false,
activeName: 'pay',
formLoading: false,
form: {
appToken: '',
id: '',
payPassword: '',
status: 1,
appId: ''
}
}
},
methods: {
// 保存
async submitHandle() {
this.formLoading = true
try {
await tbMerchantThirdApplyPut(this.form)
this.$emit('success')
this.formLoading = false
this.$notify({
title: '成功',
message: `提交成功`,
type: 'success'
});
this.close()
} catch (error) {
this.formLoading = false
console.log(error)
}
},
close() {
this.dialogVisible = false
},
reset() {
this.form.appToken = ''
this.form.id = ''
this.form.payPassword = ''
this.form.status = 1
this.form.appId = ''
},
// 详情(配置三方支付)
async getDetail(id) {
try {
const res = await tbMerchantThirdApply(id)
this.form.appToken = res.appToken
this.form.payPassword = res.payPassword
this.form.status = res.status
this.form.appId = res.appId
} catch (error) {
console.log(error)
}
},
show(obj) {
this.dialogVisible = true
if (obj && obj.id) {
this.form.id = obj.merchantId
this.getDetail(obj.merchantId)
}
}
}
})
</script>
<style scoped lang="scss">
::v-deep(.el-dialog__header) {
padding: 0;
}
</style>

View File

@@ -0,0 +1,408 @@
<template>
<div>
<div>
<el-form
ref="form"
:model="form"
:rules="rules"
label-width="120px"
label-position="left"
>
<el-form-item label="门店名称" prop="shopName">
<el-input
v-model="form.shopName"
placeholder="请输入门店名称"
style="width: 500px;"
></el-input>
</el-form-item>
<el-form-item label="门店logo">
<el-image
:src="form.logo || require('@/assets/images/upload.png')"
fit="contain"
style="width: 80px;height: 80px;"
@click="
showUpload = true;
uploadIndex = 1;
"
></el-image>
</el-form-item>
<el-form-item label="门店照片">
<el-image
:src="form.coverImg || require('@/assets/images/upload.png')"
fit="contain"
style="width: 80px;height: 80px;"
@click="
showUpload = true;
uploadIndex = 2;
"
></el-image>
</el-form-item>
<el-form-item label="微信二维码">
<el-image
:src="form.shopQrcode || require('@/assets/images/upload.png')"
fit="contain"
style="width: 80px;height: 80px;"
@click="
showUpload = true;
uploadIndex = 3;
"
></el-image>
</el-form-item>
<el-form-item label="联系电话" prop="phone">
<el-input
v-model="form.phone"
placeholder="请输入联系电话"
style="width: 500px;"
></el-input>
</el-form-item>
<!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
:min="0"></el-input-number>
</el-form-item> -->
<el-form-item label="店铺经度">
<el-row>
<el-col :span="4">
<el-input v-model="form.lng" placeholder="经度"></el-input>
</el-col>
<el-col :span="4">
<el-input
v-model="form.lat"
placeholder="纬度"
style="margin-left: 10px;"
></el-input>
</el-col>
<el-col :span="4">
<el-button
type="primary"
plain
icon="el-icon-place"
style="margin-left: 20px;"
@click="showLocation = true"
>选择坐标</el-button
>
</el-col>
</el-row>
<div style="color: #999;">准确的定位便于用户导航到店铺</div>
</el-form-item>
<el-form-item label="门店详细地址">
<el-input
type="textarea"
v-model="form.address"
placeholder="请输入门店详细地址"
style="width: 500px;"
></el-input>
</el-form-item>
<el-form-item label="营业时间">
<el-time-picker
placeholder="起始时间"
v-model="startTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59',
format: 'HH:mm'
}"
format="HH:mm"
value-format="HH:mm"
>
</el-time-picker>
<el-time-picker
placeholder="结束时间"
v-model="endTime"
:picker-options="{
selectableRange: `${startTime}:00 - 23:59:59`
}"
format="HH:mm"
value-format="HH:mm"
>
</el-time-picker>
</el-form-item>
<!-- <el-form-item label="结算类型">
<el-radio-group v-model="form.settleType">
<el-radio :label="0">今日</el-radio>
<el-radio :label="1">次日</el-radio>
</el-radio-group>
</el-form-item> -->
<!-- <el-form-item label="结算时间" prop="settleTime">
<el-time-picker
placeholder="请选择结算时间"
v-model="form.settleTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59',
format: 'HH:mm'
}"
format="HH:mm"
value-format="HH:mm"
>
</el-time-picker>
</el-form-item> -->
<el-form-item label="店铺简介">
<el-input
type="textarea"
v-model="form.detail"
placeholder="请输入店铺简介"
style="width: 500px;"
></el-input>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio :label="1">营业中</el-radio>
<el-radio :label="2">休息中</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="submitHandle"
:loading="formLoading"
>
<span v-if="!formLoading">保存</span>
<span v-else>保存中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
<el-dialog title="选择地址" :visible.sync="showLocation">
<div class="map_box">
<div class="map">
<el-amap :center="amapOptions.center">
<el-amap-marker :position="amapOptions.center"></el-amap-marker>
</el-amap>
</div>
<div class="search_box">
<el-amap-search-box
:search-option="searchOption"
:on-search-result="onSearchResult"
></el-amap-search-box>
</div>
<div class="search_wrap">
<div class="item" v-for="item in locationSearchList" :key="item.id">
<div class="left">
<div class="name">{{ item.name }}-{{ item.address }}</div>
<div class="location">经纬度{{ item.lng }},{{ item.lat }}</div>
</div>
<div class="btn">
<el-button type="primary" @click="selectLocationHandle(item)">
选择
</el-button>
</div>
</div>
</div>
</div>
</el-dialog>
<el-dialog
:visible.sync="showUpload"
:close-on-click-modal="false"
append-to-body
width="500px"
@close="showUpload = false"
>
<el-upload
:before-remove="handleBeforeRemove"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:headers="headers"
:action="qiNiuUploadApi"
class="upload-demo"
multiple
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" style="display: block;" class="el-upload__tip">
请勿上传违法文件且文件不超过15M
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
import { mapGetters } from "vuex";
import crudQiNiu from "@/api/tools/qiniu";
import { tbShopInfo, tbShopInfoPut } from "@/api/user";
export default {
computed: {
...mapGetters(["qiNiuUploadApi"])
},
data() {
return {
showLocation: false,
showUpload: false,
uploadIndex: 1,
startTime: "",
endTime: "",
formLoading: false,
form: {},
rules: {
shopName: [
{
required: true,
message: " ",
trigger: "blur"
}
],
phone: [
{
required: true,
message: " ",
trigger: "blur"
}
],
settleTime: [
{
required: true,
message: " ",
trigger: "blur"
}
]
},
fileList: [],
files: [],
headers: {
Authorization: getToken()
},
searchOption: {
city: "西安",
citylimit: false
},
locationSearchList: [],
amapOptions: {
center: [108.946465, 34.347984],
position: []
}
};
},
mounted() {
this.tbShopInfo();
},
methods: {
onSearchResult(res) {
this.locationSearchList = res;
this.amapOptions.center = [res[0].lng, res[0].lat];
},
// 确认地址选择
selectLocationHandle(item) {
this.form.lng = item.lng;
this.form.lat = item.lat;
this.showLocation = false;
},
// 获取用户详情
async tbShopInfo() {
try {
const shopId = localStorage.getItem("shopId");
const res = await tbShopInfo(shopId);
this.form = res;
if (res.businessTime) {
const businessTime = res.businessTime.split("-");
this.startTime = businessTime[0];
this.endTime = businessTime[1];
}
} catch (error) {}
},
// 保存
submitHandle() {
this.$refs.form.validate(async valid => {
if (valid) {
this.formLoading = true;
try {
if (this.startTime && this.endTime) {
this.form.businessTime = `${this.startTime}-${this.endTime}`;
}
console.log(this.startTime, this.endTime);
const res = await tbShopInfoPut(this.form);
this.formLoading = false;
this.$notify({
title: "成功",
message: "提交成功",
type: "success"
});
} catch (error) {}
}
});
},
handleSuccess(response, file, fileList) {
// const uid = file.uid
// const id = response.id
// this.files.push({ uid, id })
console.log("上传成功", response);
this.files = response.data;
},
handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) {
if (this.files[i].uid === file.uid) {
crudQiNiu.del([this.files[i].id]).then(res => {});
return true;
}
}
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
// 监听上传失败
handleError(e, file, fileList) {
const msg = JSON.parse(e.message);
this.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR);
},
// 刷新列表数据
doSubmit() {
this.fileList = [];
this.showUpload = false;
switch (this.uploadIndex) {
case 1:
this.form.logo = this.files[0];
break;
case 2:
this.form.coverImg = this.files[0];
break;
case 3:
this.form.shopQrcode = this.files[0];
break;
default:
break;
}
}
}
};
</script>
<style scoped lang="scss">
.map_box {
width: 100%;
position: relative;
.map {
height: 300px;
}
.search_box {
position: absolute;
top: 10px;
left: 10px;
}
.search_wrap {
padding: 6px 0;
.item {
display: flex;
padding: 12px 0;
.left {
flex: 1;
display: flex;
flex-direction: column;
padding-right: 20px;
.location {
color: #999;
padding-top: 4px;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,85 @@
<template>
<div>
<el-form ref="form" :model="form" label-width="120px" label-position="left">
<el-form-item label="货币单位">
<el-radio-group v-model="form.currency">
<el-radio-button label="¥"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="备用金">
<el-input v-model="form.prepareAmount" placeholder="0.00" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="保留小数位">
<el-radio-group v-model="form.decimalsDigits">
<el-radio-button label="0"></el-radio-button>
<el-radio-button label="1"></el-radio-button>
<el-radio-button label="2"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="语音通知">
<el-switch v-model="form.voiceNotification" :active-value="1" :inactive-value="0"></el-switch>
<div style="color: #999;">开启后将语音播报待处理事件</div>
</el-form-item>
<el-form-item label="移动端支付">
<el-switch v-model="form.allowWebPay" :active-value="1" :inactive-value="0"></el-switch>
<div style="color: #999;">是否允许用户在小程序端支付订单</div>
</el-form-item>
<el-form-item label="自动锁屏">
<el-select v-model="form.autoLockScreen" placeholder="请选择锁屏时间">
<el-option label="不自动锁屏" :value="0"></el-option>
<el-option label="30s" value="30"></el-option>
<el-option label="1min" value="60s"></el-option>
<el-option label="2min" value="120s"></el-option>
<el-option label="5min" value="300s"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitHandle" :loading="formLoading">
<span v-if="!formLoading">保存</span>
<span v-else>保存中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { tbShopCurrency, tbShopCurrencyPut } from '@/api/shop'
export default {
data() {
return {
formLoading: false,
form: {}
}
},
mounted() {
this.tbShopCurrency()
},
methods: {
// 保存
submitHandle() {
this.$refs.form.validate(async (valid) => {
if (valid) {
this.formLoading = true
try {
const res = await tbShopCurrencyPut(this.form)
this.formLoading = false
this.$notify({
title: '成功',
message: '提交成功',
type: 'success'
});
} catch (error) { }
}
})
},
// 获取基本配置
async tbShopCurrency() {
try {
const res = await tbShopCurrency(localStorage.getItem('shopId'))
this.form = res
} catch (error) { }
}
}
}
</script>

View File

@@ -0,0 +1,185 @@
<template>
<div class="app-container">
<div class="head-container">
<el-row :gutter="20">
<el-col :span="3">
<el-input v-model="query.name" size="small" clearable placeholder="请输入店铺名称" style="width: 100%;"
class="filter-item" @keyup.enter.native="getTableData" />
</el-col>
<el-col :span="3">
<el-input v-model="query.account" size="small" clearable placeholder="请输入商户号" style="width: 100%;"
class="filter-item" @keyup.enter.native="getTableData" />
</el-col>
<el-col :span="3">
<el-select v-model="query.status" placeholder="请选择店铺状态" style="width: 100%;">
<el-option :label="item.label" :value="item.type" v-for="item in status" :key="item.type" />
</el-select>
</el-col>
<el-col :span="6">
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-col>
</el-row>
</div>
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addShop.show()">添加店铺</el-button>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table-column label="店铺信息" width="200">
<template v-slot="scope">
<div class="shop_info">
<el-image :src="scope.row.coverImg"
style="width: 50px;height: 50px;border-radius: 4px;background-color: #efefef;">
<div class="img_error" slot="error">
<i class="icon el-icon-document-delete"></i>
</div>
</el-image>
<div class="info">
<span>{{ scope.row.shopName }}</span>
<div class="tag_wrap">
<el-tag type="info" effect="dark" v-if="scope.row.profiles == 'no'">未激活</el-tag>
<el-tag type="warning" effect="dark" v-if="scope.row.profiles == 'probation'">试用</el-tag>
<el-tag type="success" effect="dark" v-if="scope.row.profiles == 'release'">正式</el-tag>
<el-tag type="primary" effect="dark" v-if="scope.row.isWxMaIndependent">独立小程序</el-tag>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="registerType" label="类型">
<template v-slot="scope">
<span v-if="scope.row.registerType == 'munchies'">快餐版</span>
<span v-if="scope.row.registerType == 'restaurant'">餐饮版</span>
</template>
</el-table-column>
<el-table-column prop="address" label="商户号"></el-table-column>
<el-table-column prop="lowPrice" label="来源"></el-table-column>
<el-table-column prop="lowPrice" label="认证状态">-</el-table-column>
<el-table-column prop="status" label="店铺状态">
<template v-slot="scope">
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" disabled></el-switch>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="到期时间">
<template v-slot="scope">
{{ dayjs(scope.row.expireAt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template v-slot="scope">
<el-button type="text" icon="el-icon-edit" @click="$refs.addShop.show(scope.row)">编辑</el-button>
<el-dropdown @command="dropdownClick">
<el-button type="text">更多<i class="el-icon-arrow-down"></i></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ row: scope.row, command: 1 }">详情</el-dropdown-item>
<el-dropdown-item :command="2">续费记录</el-dropdown-item>
<el-dropdown-item :command="3">前往店铺</el-dropdown-item>
<el-dropdown-item :command="4">重置密码</el-dropdown-item>
<el-dropdown-item divided :command="5">删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
<addShop ref="addShop" @success="getTableData" />
<detailModal ref="detailModal" />
</div>
</template>
<script>
import dayjs from 'dayjs'
import { tbShopInfo } from '@/api/shop.js'
import addShop from '../components/addShop'
import detailModal from '../components/detailModal'
export default {
components: { addShop, detailModal },
data() {
return {
dayjs,
query: {
name: '',
account: '',
status: ''
},
status: [
{
type: 1,
label: '开启'
},
{
type: 0,
label: '关闭'
}
],
tableData: {
list: [],
page: 0,
size: 10,
loading: false,
total: 0
}
}
},
mounted() {
this.getTableData()
},
methods: {
dropdownClick(e) {
switch (e.command) {
case 1:
this.$refs.detailModal.show(e.row)
break;
default:
break;
}
},
// 重置查询
resetHandle() {
this.query.name = ''
this.query.account = ''
this.query.status = ''
this.getTableData()
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1
this.getTableData()
},
// 获取商家列表
async getTableData() {
this.tableData.loading = true
try {
const res = await tbShopInfo({
page: this.tableData.page,
size: this.tableData.size,
shopName: this.query.name,
account: this.query.account,
status: this.query.status
})
this.tableData.loading = false
this.tableData.list = res.content
this.tableData.total = res.totalElements
} catch (error) {
console.log(error)
}
}
}
}
</script>
<style scoped lang="scss">
.shop_info {
display: flex;
.info {
flex: 1;
padding-left: 4px;
}
}
</style>

View File

@@ -0,0 +1,153 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<label class="el-form-item-label">id</label>
<el-input v-model="query.id" clearable placeholder="id" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">支付方式</label>
<el-input v-model="query.payType" clearable placeholder="支付方式" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">店铺Id</label>
<el-input v-model="query.shopId" clearable placeholder="店铺Id" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">交易单号第三方交易单号</label>
<el-input v-model="query.transactionId" clearable placeholder="交易单号(第三方交易单号)" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">金额</label>
<el-input v-model="query.amount" clearable placeholder="金额" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">状态 </label>
<el-input v-model="query.status" clearable placeholder="状态 " style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">备注</label>
<el-input v-model="query.remark" clearable placeholder="备注" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">到期时间</label>
<el-input v-model="query.expiredAt" clearable placeholder="到期时间" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<label class="el-form-item-label">创建时间</label>
<el-input v-model="query.createdAt" clearable placeholder="创建时间" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation :crud="crud" />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="id">
<el-input v-model="form.id" style="width: 370px;" />
</el-form-item>
<el-form-item label="支付方式">
<el-input v-model="form.payType" style="width: 370px;" />
</el-form-item>
<el-form-item label="店铺Id">
<el-input v-model="form.shopId" style="width: 370px;" />
</el-form-item>
<el-form-item label="交易单号第三方交易单号">
<el-input v-model="form.transactionId" style="width: 370px;" />
</el-form-item>
<el-form-item label="金额" prop="amount">
<el-input v-model="form.amount" style="width: 370px;" />
</el-form-item>
<el-form-item label="状态 ">
<el-input v-model="form.status" style="width: 370px;" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" style="width: 370px;" />
</el-form-item>
<el-form-item label="到期时间">
<el-input v-model="form.expiredAt" style="width: 370px;" />
</el-form-item>
<el-form-item label="创建时间">
<el-input v-model="form.createdAt" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="id" />
<el-table-column prop="payType" label="支付方式">
<template slot-scope="scope">
{{ dict.label.pay_type[scope.row.payType] }}
</template>
</el-table-column>
<el-table-column prop="shopId" label="店铺Id" />
<el-table-column prop="transactionId" label="交易单号第三方交易单号" />
<el-table-column prop="amount" label="金额" />
<el-table-column prop="status" label="状态 ">
<template slot-scope="scope">
{{ dict.label.renewals_status[scope.row.status] }}
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" />
<el-table-column prop="expiredAt" label="到期时间" />
<el-table-column prop="createdAt" label="创建时间" />
<el-table-column v-if="checkPer(['admin','tbRenewalsPayLog:edit','tbRenewalsPayLog:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudTbRenewalsPayLog from '@/api/tbRenewalsPayLog'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, payType: null, shopId: null, orderId: null, openId: null, userId: null, transactionId: null, amount: null, status: null, remark: null, attach: null, expiredAt: null, createdAt: null, updatedAt: null }
export default {
name: 'TbRenewalsPayLog',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
dicts: ['pay_type', 'renewals_status'],
cruds() {
return CRUD({ title: '/shop/renewals', url: 'api/tbRenewalsPayLog', idField: 'id', sort: 'id,desc', crudMethod: { ...crudTbRenewalsPayLog }})
},
data() {
return {
permission: {
add: ['admin', 'tbRenewalsPayLog:add'],
edit: ['admin', 'tbRenewalsPayLog:edit'],
del: ['admin', 'tbRenewalsPayLog:del']
},
rules: {
amount: [
{ required: true, message: '金额不能为空', trigger: 'blur' }
]
},
queryTypeOptions: [
{ key: 'id', display_name: 'id' },
{ key: 'payType', display_name: '支付方式' },
{ key: 'shopId', display_name: '店铺Id' },
{ key: 'transactionId', display_name: '交易单号(第三方交易单号)' },
{ key: 'amount', display_name: '金额' },
{ key: 'status', display_name: '状态 ' },
{ key: 'remark', display_name: '备注' },
{ key: 'expiredAt', display_name: '到期时间' },
{ key: 'createdAt', display_name: '创建时间' }
]
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,26 @@
<template>
<div class="app-container">
<el-tabs v-model="activeName" type="card">
<el-tab-pane label="店铺信息" name="1"></el-tab-pane>
<el-tab-pane label="基础配置" name="2"></el-tab-pane>
</el-tabs>
<shopInfo v-if="activeName == 1" />
<shopSetting v-if="activeName == 2" />
</div>
</template>
<script>
import shopInfo from './components/shopInfo'
import shopSetting from './components/shopSetting'
export default {
components: {
shopInfo,
shopSetting
},
data() {
return {
activeName: '1',
}
}
}
</script>

View File

@@ -0,0 +1,254 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="部门名称" prop="name">
<el-input v-model="form.name" style="width: 370px;" />
</el-form-item>
<el-form-item label="部门排序" prop="deptSort">
<el-input-number
v-model.number="form.deptSort"
:min="0"
:max="999"
controls-position="right"
style="width: 370px;"
/>
</el-form-item>
<el-form-item label="顶级部门">
<el-radio-group v-model="form.isTop" style="width: 140px">
<el-radio label="1">是</el-radio>
<el-radio label="0">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="状态" prop="enabled">
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
</el-form-item>
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
<treeselect
v-model="form.pid"
:load-options="loadDepts"
:options="depts"
style="width: 370px;"
placeholder="选择上级类目"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table
ref="table"
v-loading="crud.loading"
lazy
:load="getDeptDatas"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
:data="crud.data"
row-key="id"
@select="crud.selectChange"
@select-all="crud.selectAllChange"
@selection-change="crud.selectionChangeHandler"
>
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column label="名称" prop="name" />
<el-table-column label="排序" prop="deptSort" />
<el-table-column label="状态" align="center" prop="enabled">
<template slot-scope="scope">
<el-switch
v-model="scope.row.enabled"
:disabled="scope.row.id === 1"
active-color="#409EFF"
inactive-color="#F56C6C"
@change="changeEnabled(scope.row, scope.row.enabled,)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
:disabled-dle="scope.row.id === 1"
msg="确定删除吗,如果存在下级节点则一并删除此操作不能撤销"
/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import crudDept from '@/api/system/dept'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
export default {
name: 'Dept',
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '部门', url: 'api/dept', crudMethod: { ...crudDept }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
// 设置数据字典
dicts: ['dept_status'],
data() {
return {
depts: [],
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
deptSort: [
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
]
},
permission: {
add: ['admin', 'dept:add'],
edit: ['admin', 'dept:edit'],
del: ['admin', 'dept:del']
},
enabledTypeOptions: [
{ key: 'true', display_name: '正常' },
{ key: 'false', display_name: '禁用' }
]
}
},
methods: {
getDeptDatas(tree, treeNode, resolve) {
const params = { pid: tree.id }
setTimeout(() => {
crudDept.getDepts(params).then(res => {
resolve(res.content)
})
}, 100)
},
// 新增与编辑前做的操作
[CRUD.HOOK.afterToCU](crud, form) {
if (form.pid !== null) {
form.isTop = '0'
} else if (form.id !== null) {
form.isTop = '1'
}
form.enabled = `${form.enabled}`
if (form.id != null) {
this.getSupDepts(form.id)
} else {
this.getDepts()
}
},
getSupDepts(id) {
crudDept.getDeptSuperior(id).then(res => {
const date = res.content
this.buildDepts(date)
this.depts = date
})
},
buildDepts(depts) {
depts.forEach(data => {
if (data.children) {
this.buildDepts(data.children)
}
if (data.hasChildren && !data.children) {
data.children = null
}
})
},
getDepts() {
crudDept.getDepts({ enabled: true }).then(res => {
this.depts = res.content.map(function(obj) {
if (obj.hasChildren) {
obj.children = null
}
return obj
})
})
},
// 获取弹窗内部门数据
loadDepts({ action, parentNode, callback }) {
if (action === LOAD_CHILDREN_OPTIONS) {
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
parentNode.children = res.content.map(function(obj) {
if (obj.hasChildren) {
obj.children = null
}
return obj
})
setTimeout(() => {
callback()
}, 100)
})
}
},
// 提交前的验证
[CRUD.HOOK.afterValidateCU]() {
if (this.form.pid !== null && this.form.pid === this.form.id) {
this.$message({
message: '上级部门不能为空',
type: 'warning'
})
return false
}
if (this.form.isTop === '1') {
this.form.pid = null
}
return true
},
// 改变状态
changeEnabled(data, val) {
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
crudDept.edit(data).then(res => {
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
}).catch(err => {
data.enabled = !data.enabled
console.log(err.response.data.message)
})
}).catch(() => {
data.enabled = !data.enabled
})
},
checkboxT(row, rowIndex) {
return row.id !== 1
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
height: 30px;
line-height: 30px;
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>

View File

@@ -0,0 +1,115 @@
<template>
<div>
<div v-if="query.dictName === ''">
<div class="my-code">点击字典查看详情</div>
</div>
<div v-else>
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.label" clearable size="small" placeholder="输入字典标签查询" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
<rrOperation />
</div>
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="字典标签" prop="label">
<el-input v-model="form.label" style="width: 370px;" />
</el-form-item>
<el-form-item label="字典值" prop="value">
<el-input v-model="form.value" style="width: 370px;" />
</el-form-item>
<el-form-item label="排序" prop="dictSort">
<el-input-number v-model.number="form.dictSort" :min="0" :max="999" controls-position="right" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column label="所属字典">
{{ query.dictName }}
</el-table-column>
<el-table-column prop="label" label="字典标签" />
<el-table-column prop="value" label="字典值" />
<el-table-column prop="dictSort" label="排序" />
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudDictDetail from '@/api/system/dictDetail'
import CRUD, { presenter, header, form } from '@crud/crud'
import pagination from '@crud/Pagination'
import rrOperation from '@crud/RR.operation'
import udOperation from '@crud/UD.operation'
const defaultForm = { id: null, label: null, value: null, dictSort: 999 }
export default {
components: { pagination, rrOperation, udOperation },
cruds() {
return [
CRUD({ title: '字典详情', url: 'api/dictDetail', query: { dictName: '' }, sort: ['dictSort,asc', 'id,desc'],
crudMethod: { ...crudDictDetail },
optShow: {
add: true,
edit: true,
del: true,
reset: false
},
queryOnPresenterCreated: false
})
]
},
mixins: [
presenter(),
header(),
form(function() {
return Object.assign({ dict: { id: this.dictId }}, defaultForm)
})],
data() {
return {
dictId: null,
rules: {
label: [
{ required: true, message: '请输入字典标签', trigger: 'blur' }
],
value: [
{ required: true, message: '请输入字典值', trigger: 'blur' }
],
dictSort: [
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
]
},
permission: {
add: ['admin', 'dict:add'],
edit: ['admin', 'dict:edit'],
del: ['admin', 'dict:del']
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>

View File

@@ -0,0 +1,135 @@
<template>
<div class="app-container">
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="字典名称" prop="name">
<el-input v-model="form.name" style="width: 370px;" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="form.description" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!-- 字典列表 -->
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="10" :lg="11" :xl="11" style="margin-bottom: 10px">
<el-card class="box-card">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable size="small" placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="name" label="名称" />
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</el-card>
</el-col>
<!-- 字典详情列表 -->
<el-col :xs="24" :sm="24" :md="14" :lg="13" :xl="13">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>字典详情</span>
<el-button
v-if="checkPer(['admin','dict:add']) && this.$refs.dictDetail && this.$refs.dictDetail.query.dictName"
class="filter-item"
size="mini"
style="float: right;padding: 4px 10px"
type="primary"
icon="el-icon-plus"
@click="$refs.dictDetail && $refs.dictDetail.crud.toAdd()"
>新增</el-button>
</div>
<dictDetail ref="dictDetail" :permission="permission" />
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import dictDetail from './dictDetail'
import crudDict from '@/api/system/dict'
import CRUD, { presenter, header, form } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import rrOperation from '@crud/RR.operation'
import udOperation from '@crud/UD.operation'
const defaultForm = { id: null, name: null, description: null, dictDetails: [] }
export default {
name: 'Dict',
components: { crudOperation, pagination, rrOperation, udOperation, dictDetail },
cruds() {
return [
CRUD({ title: '字典', url: 'api/dict', crudMethod: { ...crudDict }})
]
},
mixins: [presenter(), header(), form(defaultForm)],
data() {
return {
queryTypeOptions: [
{ key: 'name', display_name: '字典名称' },
{ key: 'description', display_name: '描述' }
],
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
]
},
permission: {
add: ['admin', 'dict:add'],
edit: ['admin', 'dict:edit'],
del: ['admin', 'dict:del']
}
}
},
methods: {
// 获取数据前设置好接口地址
[CRUD.HOOK.beforeRefresh]() {
if (this.$refs.dictDetail) {
this.$refs.dictDetail.query.dictName = ''
}
return true
},
// 选中字典后,设置字典详情数据
handleCurrentChange(val) {
if (val) {
this.$refs.dictDetail.query.dictName = val.name
this.$refs.dictDetail.dictId = val.id
this.$refs.dictDetail.crud.toQuery()
}
},
// 编辑前将字典明细临时清空,避免日志入库数据过长
[CRUD.HOOK.beforeToEdit](crud, form) {
// 将角色的菜单清空,避免日志入库数据过长
form.dictDetails = null
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,116 @@
<template>
<el-dialog :title="form.id ? '编辑' : '添加'" :visible.sync="dialogVisible" @close="reset">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-form-item label="字典名称" prop="name">
<el-input v-model="form.name" placeholder="请输入分类名称"></el-input>
</el-form-item>
<el-form-item label="字典描述" prop="description">
<el-input v-model="form.description" placeholder="请输入分类名称"></el-input>
</el-form-item>
<el-form-item label="字典标签" prop="dictName">
<el-input v-model="form.dictName" placeholder="请输入分类名称"></el-input>
</el-form-item>
<!-- <el-form-item label="开关">
<el-switch
v-model="form.isShow"
:active-value="1"
:inactive-value="0"
></el-switch>
</el-form-item>
<el-form-item label="排序">
<el-input-number
v-model="form.sort"
controls-position="right"
:min="0"
></el-input-number>
</el-form-item> -->
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="onSubmitHandle"> </el-button>
</span>
</el-dialog>
</template>
<script>
import { dict } from "@/api/setting";
export default {
data() {
return {
dialogVisible: false,
form: {
id: "",
pid: "",
dictName: "",
description: "",
name: ""
},
resetForm: "",
rules: {
dictName: [
{
required: true,
message: " ",
trigger: "blur"
}
],
description: [
{
required: true,
message: " ",
trigger: "blur"
}
],
name: [
{
required: true,
message: " ",
trigger: "blur"
}
]
}
};
},
mounted() {
this.resetForm = { ...this.form };
},
methods: {
onSubmitHandle() {
console.log(this.form);
this.$refs.form.validate(async valid => {
if (valid) {
try {
this.form.shopId = localStorage.getItem("shopId");
let res = await dict(this.form, this.form.id ? "put" : "post");
this.$emit("success", res);
this.close();
this.$notify({
title: "成功",
message: `${this.form.id ? "编辑" : "添加"}成功`,
type: "success"
});
} catch (error) {
console.log(error);
}
}
});
},
show(obj) {
// console.log(obj)
this.dialogVisible = true;
if (obj && obj.pid) {
this.form.pid = obj.pid;
}
if (obj && obj.id) {
this.form = { ...obj };
}
},
close() {
this.dialogVisible = false;
},
reset() {
this.form = { ...this.resetForm };
}
}
};
</script>

View File

@@ -0,0 +1,124 @@
<template>
<div class="app-container">
<div class="head-container">
<el-row :gutter="20">
<el-col :span="6">
<el-input v-model="query.blurry" size="small" clearable placeholder="请输入名称或描述" style="width: 100%;"
class="filter-item" @keyup.enter.native="getTableData" />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-col>
</el-row>
</div>
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addRef.show()">
添加字典
</el-button>
<add ref="addRef" @success="getTableData" />
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading" row-key="id"
:tree-props="{ children: 'childrenList' }">
<el-table-column type="index" width="50">
</el-table-column>
<el-table-column label="名称" prop="name"></el-table-column>
<el-table-column label="描述" prop="description"></el-table-column>
<el-table-column label="标签" prop="dictName"></el-table-column>
<el-table-column label="操作" width="300">
<template v-slot="scope">
<el-button type="text" size="mini" round icon="el-icon-plus"
@click="$refs.addRef.show({ pid: scope.row.id })" v-if="!scope.row.pid">添加下级字典</el-button>
<el-button type="text" size="mini" round icon="el-icon-edit"
@click="$refs.addRef.show(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="delHandle([scope.row.id])">
<el-button type="text" size="mini" round icon="el-icon-delete" slot="reference">
删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page + 1" :page-size="tableData.size"
@current-change="paginationChange" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</template>
<script>
import add from "./components/add";
import { dict } from "@/api/setting";
export default {
components: {
add
},
data() {
return {
query: { blurry: "" },
tableData: {
page: 0,
size: 10,
total: 0,
loading: false,
list: []
}
};
},
mounted() {
this.getTableData();
},
methods: {
// 添加子分类
addChildGatgory(row) { },
// 查询table
toQuery() {
this.getTableData();
}, // 重置查询
resetHandle() {
this.tableData.page = 0;
this.query.blurry = "";
this.getTableData();
},
// 分页回调
paginationChange(e) {
this.tableData.page = e - 1;
this.getTableData();
},
// 删除
async delHandle(ids) {
try {
await dict(ids, "delete");
this.$notify({
title: "成功",
message: `删除成功`,
type: "success"
});
this.getTableData();
} catch (error) {
console.log(error);
}
},
// 获取商品列表
async getTableData() {
this.tableData.loading = true;
try {
const res = await dict(
{
page: this.tableData.page,
size: this.tableData.size,
blurry: this.query.blurry
}
);
this.tableData.loading = false;
this.tableData.list = res.content;
this.tableData.total = res.totalElements;
} catch (error) {
console.log(error);
}
}
}
};
</script>

View File

@@ -0,0 +1,110 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<eHeader :dict="dict" :permission="permission" />
<crudOperation :permission="permission" />
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="jobSort" label="排序">
<template slot-scope="scope">
{{ scope.row.jobSort }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" align="center">
<template slot-scope="scope">
<el-switch
v-model="scope.row.enabled"
active-color="#409EFF"
inactive-color="#F56C6C"
@change="changeEnabled(scope.row, scope.row.enabled)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建日期" />
<!-- 编辑与删除 -->
<el-table-column
v-if="checkPer(['admin','job:edit','job:del'])"
label="操作"
width="130px"
align="center"
fixed="right"
>
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
<!--表单渲染-->
<eForm :job-status="dict.job_status" />
</div>
</template>
<script>
import crudJob from '@/api/system/job'
import eHeader from './module/header'
import eForm from './module/form'
import CRUD, { presenter } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import udOperation from '@crud/UD.operation'
export default {
name: 'Job',
components: { eHeader, eForm, crudOperation, pagination, udOperation },
cruds() {
return CRUD({
title: '岗位',
url: 'api/job',
sort: ['jobSort,asc', 'id,desc'],
crudMethod: { ...crudJob }
})
},
mixins: [presenter()],
// 数据字典
dicts: ['job_status'],
data() {
return {
permission: {
add: ['admin', 'job:add'],
edit: ['admin', 'job:edit'],
del: ['admin', 'job:del']
}
}
},
methods: {
// 改变状态
changeEnabled(data, val) {
this.$confirm('此操作将 "' + this.dict.label.job_status[val] + '" ' + data.name + '岗位, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// eslint-disable-next-line no-undef
crudJob.edit(data).then(() => {
// eslint-disable-next-line no-undef
this.crud.notify(this.dict.label.job_status[val] + '成功', 'success')
}).catch(err => {
data.enabled = !data.enabled
console.log(err.data.message)
})
}).catch(() => {
data.enabled = !data.enabled
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<el-dialog
append-to-body
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible="crud.status.cu > 0"
:title="crud.status.title"
width="500px"
>
<el-form
ref="form"
:model="form"
:rules="rules"
size="small"
label-width="80px"
>
<el-form-item
label="名称"
prop="name"
>
<el-input
v-model="form.name"
style="width: 370px;"
/>
</el-form-item>
<el-form-item
label="排序"
prop="jobSort"
>
<el-input-number
v-model.number="form.jobSort"
:min="0"
:max="999"
controls-position="right"
style="width: 370px;"
/>
</el-form-item>
<el-form-item
v-if="form.pid !== 0"
label="状态"
prop="enabled"
>
<el-radio
v-for="item in jobStatus"
:key="item.id"
v-model="form.enabled"
:label="item.value === 'true'"
>
{{ item.label }}
</el-radio>
</el-form-item>
</el-form>
<div
slot="footer"
class="dialog-footer"
>
<el-button
type="text"
@click="crud.cancelCU"
>
取消
</el-button>
<el-button
:loading="crud.status.cu === 2"
type="primary"
@click="crud.submitCU"
>
确认
</el-button>
</div>
</el-dialog>
</template>
<script>
import { form } from '@crud/crud'
const defaultForm = {
id: null,
name: '',
jobSort: 999,
enabled: true
}
export default {
mixins: [form(defaultForm)],
props: {
jobStatus: {
type: Array,
required: true
}
},
data() {
return {
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
jobSort: [
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
]
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>

View File

@@ -0,0 +1,32 @@
<template>
<div
v-if="crud.props.searchToggle"
>
<el-input v-model="query.name" clearable size="small" placeholder="输入岗位名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
<el-option v-for="item in dict.dict.job_status" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<rrOperation />
</div>
</template>
<script>
import { header } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { rrOperation, DateRangePicker },
mixins: [header()],
props: {
dict: {
type: Object,
required: true
},
permission: {
type: Object,
required: true
}
}
}
</script>

View File

@@ -0,0 +1,244 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" style="width: 200px;"
class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!--表单渲染-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0" :title="crud.status.title" width="680px">
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="form.type" size="mini" style="width: 178px">
<el-radio-button label="0">目录</el-radio-button>
<el-radio-button label="1">菜单</el-radio-button>
<el-radio-button label="2">按钮</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-show="form.type.toString() !== '2'" label="菜单图标" prop="icon">
<el-popover placement="bottom-start" width="450" trigger="click" @show="$refs['iconSelect'].reset()">
<IconSelect ref="iconSelect" @selected="selected" />
<el-input slot="reference" v-model="form.icon" style="width: 450px;" 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 v-show="form.type.toString() !== '2'" label="外链菜单" prop="iFrame">
<el-radio-group v-model="form.iFrame" size="mini">
<el-radio-button label="true">是</el-radio-button>
<el-radio-button label="false">否</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-show="form.type.toString() === '1'" label="菜单缓存" prop="cache">
<el-radio-group v-model="form.cache" size="mini">
<el-radio-button label="true">是</el-radio-button>
<el-radio-button label="false">否</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-show="form.type.toString() !== '2'" label="菜单可见" prop="hidden">
<el-radio-group v-model="form.hidden" size="mini">
<el-radio-button label="false">是</el-radio-button>
<el-radio-button label="true">否</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.type.toString() !== '2'" label="菜单标题" prop="title">
<el-input v-model="form.title" :style="form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'"
placeholder="菜单标题" />
</el-form-item>
<el-form-item v-if="form.type.toString() === '2'" label="按钮名称" prop="title">
<el-input v-model="form.title" placeholder="按钮名称" style="width: 178px;" />
</el-form-item>
<el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission">
<el-input v-model="form.permission" :disabled="form.iFrame.toString() === 'true'" placeholder="权限标识"
style="width: 178px;" />
</el-form-item>
<el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path">
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" />
</el-form-item>
<el-form-item label="菜单排序" prop="menuSort">
<el-input-number v-model.number="form.menuSort" :min="0" :max="999" controls-position="right"
style="width: 178px;" />
</el-form-item>
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件名称"
prop="componentName">
<el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" />
</el-form-item>
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件路径"
prop="component">
<el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" />
</el-form-item>
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="选中父级菜单">
<el-input v-model="form.activeMenu" placeholder="请输入父级菜单path" style="width: 178px;" />
</el-form-item>
<el-form-item label="上级类目" prop="pid">
<treeselect v-model="form.pid" :options="menus" :load-options="loadMenus" style="width: 450px;"
placeholder="选择上级类目" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" lazy :load="getMenus" :data="crud.data"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" row-key="id" @select="crud.selectChange"
@select-all="crud.selectAllChange" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" label="菜单标题" width="125px" prop="title" />
<el-table-column prop="icon" label="图标" align="center" width="60px">
<template slot-scope="scope">
<svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" />
</template>
</el-table-column>
<el-table-column prop="menuSort" align="center" label="排序">
<template slot-scope="scope">
{{ scope.row.menuSort }}
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="permission" label="权限标识" />
<el-table-column :show-overflow-tooltip="true" prop="component" label="组件路径" />
<el-table-column prop="iFrame" label="外链" width="75px">
<template slot-scope="scope">
<span v-if="scope.row.iFrame">是</span>
<span v-else>否</span>
</template>
</el-table-column>
<el-table-column prop="cache" label="缓存" width="75px">
<template slot-scope="scope">
<span v-if="scope.row.cache">是</span>
<span v-else>否</span>
</template>
</el-table-column>
<el-table-column prop="hidden" label="可见" width="75px">
<template slot-scope="scope">
<span v-if="scope.row.hidden">否</span>
<span v-else>是</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建日期" width="135px" />
<el-table-column v-if="checkPer(['admin', 'menu:edit', 'menu:del'])" label="操作" width="130px" align="center"
fixed="right">
<template slot-scope="scope">
<udOperation :data="scope.row" :permission="permission" msg="确定删除吗,如果存在下级节点则一并删除此操作不能撤销" />
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import crudMenu from '@/api/system/menu'
import IconSelect from '@/components/IconSelect'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import DateRangePicker from '@/components/DateRangePicker'
// crud交由presenter持有
const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iFrame: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
export default {
name: 'Menu',
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '菜单', url: 'api/menus', crudMethod: { ...crudMenu } })
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
menus: [],
permission: {
add: ['admin', 'menu:add'],
edit: ['admin', 'menu:edit'],
del: ['admin', 'menu:del']
},
rules: {
title: [
{ required: true, message: '请输入标题', trigger: 'blur' }
],
path: [
{ required: true, message: '请输入地址', trigger: 'blur' }
]
}
}
},
methods: {
// 新增与编辑前做的操作
[CRUD.HOOK.afterToCU](crud, form) {
this.menus = []
if (form.id != null) {
if (form.pid === null) {
form.pid = 0
}
this.getSupDepts(form.id)
} else {
this.menus.push({ id: 0, label: '顶级类目', children: null })
}
},
getMenus(tree, treeNode, resolve) {
const params = { pid: tree.id }
setTimeout(() => {
crudMenu.getMenus(params).then(res => {
resolve(res.content)
})
}, 100)
},
getSupDepts(id) {
crudMenu.getMenuSuperior(id).then(res => {
const children = res.map(function (obj) {
if (!obj.leaf && !obj.children) {
obj.children = null
}
return obj
})
this.menus = [{ id: 0, label: '顶级类目', children: children }]
})
},
loadMenus({ action, parentNode, callback }) {
if (action === LOAD_CHILDREN_OPTIONS) {
crudMenu.getMenusTree(parentNode.id).then(res => {
parentNode.children = res.map(function (obj) {
if (!obj.leaf) {
obj.children = null
}
return obj
})
setTimeout(() => {
callback()
}, 100)
})
}
},
// 选中图标
selected(name) {
this.form.icon = name
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .vue-treeselect__control,
::v-deep .vue-treeselect__placeholder,
::v-deep .vue-treeselect__single-value {
height: 30px;
line-height: 30px;
}
</style>

View File

@@ -0,0 +1,360 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission" />
</div>
<!-- 表单渲染 -->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="520px">
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="角色名称" prop="name">
<el-input v-model="form.name" style="width: 380px;" />
</el-form-item>
<el-form-item label="角色级别" prop="level">
<el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" />
</el-form-item>
<el-form-item label="数据范围" prop="dataScope">
<el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope">
<el-option
v-for="item in dateScopes"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts">
<treeselect
v-model="deptDatas"
:load-options="loadDepts"
:options="depts"
multiple
style="width: 380px"
placeholder="请选择"
/>
</el-form-item>
<el-form-item label="描述信息" prop="description">
<el-input v-model="form.description" style="width: 380px;" rows="5" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<el-row :gutter="15">
<!--角色管理-->
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="17" style="margin-bottom: 10px">
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span class="role-span">角色列表</span>
</div>
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="dataScope" label="数据权限" />
<el-table-column prop="level" label="角色级别" />
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
<el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','roles:edit','roles:del'])" label="操作" width="130px" align="center" fixed="right">
<template slot-scope="scope">
<udOperation
v-if="scope.row.level >= level"
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</el-card>
</el-col>
<!-- 菜单授权 -->
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="7">
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top">
<span class="role-span">菜单分配</span>
</el-tooltip>
<el-button
v-permission="['admin','roles:edit']"
:disabled="!showButton"
:loading="menuLoading"
icon="el-icon-check"
size="mini"
style="float: right; padding: 6px 9px"
type="primary"
@click="saveMenu"
>保存</el-button>
</div>
<el-tree
ref="menu"
lazy
:data="menus"
:default-checked-keys="menuIds"
:load="getMenuDatas"
:props="defaultProps"
check-strictly
accordion
show-checkbox
node-key="id"
@check="menuChange"
/>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import crudRoles from '@/api/system/role'
import { getDepts, getDeptSuperior } from '@/api/system/dept'
import { getMenusTree, getChild } from '@/api/system/menu'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 }
export default {
name: 'Role',
components: { Treeselect, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '角色', url: 'api/roles', sort: 'level,asc', crudMethod: { ...crudRoles }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' },
dateScopes: ['全部', '本级', '自定义'], level: 3,
currentId: 0, menuLoading: false, showButton: false,
menus: [], menuIds: [], depts: [], deptDatas: [], // 多选时使用
permission: {
add: ['admin', 'roles:add'],
edit: ['admin', 'roles:edit'],
del: ['admin', 'roles:del']
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
permission: [
{ required: true, message: '请输入权限', trigger: 'blur' }
]
}
}
},
created() {
crudRoles.getLevel().then(data => {
this.level = data.level
})
},
methods: {
getMenuDatas(node, resolve) {
setTimeout(() => {
getMenusTree(node.data.id ? node.data.id : 0).then(res => {
resolve(res)
})
}, 100)
},
[CRUD.HOOK.afterRefresh]() {
this.$refs.menu.setCheckedKeys([])
},
// 新增前初始化部门信息
[CRUD.HOOK.beforeToAdd](crud, form) {
this.deptDatas = []
form.menus = null
},
// 编辑前初始化自定义数据权限的部门信息
[CRUD.HOOK.beforeToEdit](crud, form) {
this.deptDatas = []
if (form.dataScope === '自定义') {
this.getSupDepts(form.depts)
}
const _this = this
form.depts.forEach(function(dept) {
_this.deptDatas.push(dept.id)
})
// 将角色的菜单清空,避免日志入库数据过长
form.menus = null
},
// 提交前做的操作
[CRUD.HOOK.afterValidateCU](crud) {
if (crud.form.dataScope === '自定义' && this.deptDatas.length === 0) {
this.$message({
message: '自定义数据权限不能为空',
type: 'warning'
})
return false
} else if (crud.form.dataScope === '自定义') {
const depts = []
this.deptDatas.forEach(function(data) {
const dept = { id: data }
depts.push(dept)
})
crud.form.depts = depts
} else {
crud.form.depts = []
}
return true
},
// 触发单选
handleCurrentChange(val) {
if (val) {
const _this = this
// 清空菜单的选中
this.$refs.menu.setCheckedKeys([])
// 保存当前的角色id
this.currentId = val.id
// 初始化默认选中的key
this.menuIds = []
val.menus.forEach(function(data) {
_this.menuIds.push(data.id)
})
this.showButton = true
}
},
menuChange(menu) {
// 获取该节点的所有子节点id 包含自身
getChild(menu.id).then(childIds => {
// 判断是否在 menuIds 中,如果存在则删除,否则添加
if (this.menuIds.indexOf(menu.id) !== -1) {
for (let i = 0; i < childIds.length; i++) {
const index = this.menuIds.indexOf(childIds[i])
if (index !== -1) {
this.menuIds.splice(index, 1)
}
}
} else {
for (let i = 0; i < childIds.length; i++) {
const index = this.menuIds.indexOf(childIds[i])
if (index === -1) {
this.menuIds.push(childIds[i])
}
}
}
this.$refs.menu.setCheckedKeys(this.menuIds)
})
},
// 保存菜单
saveMenu() {
this.menuLoading = true
const role = { id: this.currentId, menus: [] }
// 得到已选中的 key 值
this.menuIds.forEach(function(id) {
const menu = { id: id }
role.menus.push(menu)
})
crudRoles.editMenu(role).then(() => {
this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
this.menuLoading = false
this.update()
}).catch(err => {
this.menuLoading = false
console.log(err.response.data.message)
})
},
// 改变数据
update() {
// 无刷新更新 表格数据
crudRoles.get(this.currentId).then(res => {
for (let i = 0; i < this.crud.data.length; i++) {
if (res.id === this.crud.data[i].id) {
this.crud.data[i] = res
break
}
}
})
},
// 获取部门数据
getDepts() {
getDepts({ enabled: true }).then(res => {
this.depts = res.content.map(function(obj) {
if (obj.hasChildren) {
obj.children = null
}
return obj
})
})
},
getSupDepts(depts) {
const ids = []
depts.forEach(dept => {
ids.push(dept.id)
})
getDeptSuperior(ids).then(res => {
const date = res.content
this.buildDepts(date)
this.depts = date
})
},
buildDepts(depts) {
depts.forEach(data => {
if (data.children) {
this.buildDepts(data.children)
}
if (data.hasChildren && !data.children) {
data.children = null
}
})
},
// 获取弹窗内部门数据
loadDepts({ action, parentNode, callback }) {
if (action === LOAD_CHILDREN_OPTIONS) {
getDepts({ enabled: true, pid: parentNode.id }).then(res => {
parentNode.children = res.content.map(function(obj) {
if (obj.hasChildren) {
obj.children = null
}
return obj
})
setTimeout(() => {
callback()
}, 200)
})
}
},
// 如果数据权限为自定义则获取部门数据
changeScope() {
if (this.form.dataScope === '自定义') {
this.getDepts()
}
},
checkboxT(row) {
return row.level >= this.level
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.role-span {
font-weight: bold;color: #303133;
font-size: 15px;
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
::v-deep .vue-treeselect__multi-value{
margin-bottom: 0;
}
::v-deep .vue-treeselect__multi-value-item{
border: 0;
padding: 0;
}
</style>

View File

@@ -0,0 +1,210 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<!-- 任务日志 -->
<el-button
slot="right"
class="filter-item"
size="mini"
type="info"
icon="el-icon-tickets"
@click="doLog"
>日志</el-button>
</crudOperation>
<Log ref="log" />
</div>
<!--Form表单-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="730px">
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="任务名称" prop="jobName">
<el-input v-model="form.jobName" style="width: 220px;" />
</el-form-item>
<el-form-item label="任务描述" prop="description">
<el-input v-model="form.description" style="width: 220px;" />
</el-form-item>
<el-form-item label="Bean名称" prop="beanName">
<el-input v-model="form.beanName" style="width: 220px;" />
</el-form-item>
<el-form-item label="执行方法" prop="methodName">
<el-input v-model="form.methodName" style="width: 220px;" />
</el-form-item>
<el-form-item label="Cron表达式" prop="cronExpression">
<el-input v-model="form.cronExpression" style="width: 220px;" />
</el-form-item>
<el-form-item label="子任务ID">
<el-input v-model="form.subTask" placeholder="多个用逗号隔开按顺序执行" style="width: 220px;" />
</el-form-item>
<el-form-item label="任务负责人" prop="personInCharge">
<el-input v-model="form.personInCharge" style="width: 220px;" />
</el-form-item>
<el-form-item label="告警邮箱" prop="email">
<el-input v-model="form.email" placeholder="多个邮箱用逗号隔开" style="width: 220px;" />
</el-form-item>
<el-form-item label="失败后暂停">
<el-radio-group v-model="form.pauseAfterFailure" style="width: 220px">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="任务状态">
<el-radio-group v-model="form.isPause" style="width: 220px">
<el-radio :label="false">启用</el-radio>
<el-radio :label="true">暂停</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="参数内容">
<el-input v-model="form.params" style="width: 556px;" rows="4" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column :selectable="checkboxT" type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="id" label="任务ID" />
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
<el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
<el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
<el-table-column :show-overflow-tooltip="true" prop="params" label="参数" />
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
<el-table-column :show-overflow-tooltip="true" prop="isPause" width="90px" label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{ scope.row.isPause ? '已暂停' : '运行中' }}</el-tag>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="description" width="150px" label="描述" />
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="136px" label="创建日期" />
<el-table-column v-if="checkPer(['admin','timing:edit','timing:del'])" label="操作" width="170px" align="center" fixed="right">
<template slot-scope="scope">
<el-button v-permission="['admin','timing:edit']" size="mini" style="margin-right: 3px;" type="text" @click="crud.toEdit(scope.row)">编辑</el-button>
<el-button v-permission="['admin','timing:edit']" style="margin-left: -2px" type="text" size="mini" @click="execute(scope.row.id)">执行</el-button>
<el-button v-permission="['admin','timing:edit']" style="margin-left: 3px" type="text" size="mini" @click="updateStatus(scope.row.id,scope.row.isPause ? '恢复' : '暂停')">
{{ scope.row.isPause ? '恢复' : '暂停' }}
</el-button>
<el-popover
:ref="scope.row.id"
v-permission="['admin','timing:del']"
placement="top"
width="200"
>
<p>确定停止并删除该任务吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
</div>
<el-button slot="reference" type="text" size="mini">删除</el-button>
</el-popover>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudJob from '@/api/system/timing'
import Log from './log'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, jobName: null, subTask: null, beanName: null, methodName: null, params: null, cronExpression: null, pauseAfterFailure: true, isPause: false, personInCharge: null, email: null, description: null }
export default {
name: 'Timing',
components: { Log, pagination, crudOperation, rrOperation, DateRangePicker },
cruds() {
return CRUD({ title: '定时任务', url: 'api/jobs', crudMethod: { ...crudJob }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
delLoading: false,
permission: {
add: ['admin', 'timing:add'],
edit: ['admin', 'timing:edit'],
del: ['admin', 'timing:del']
},
rules: {
jobName: [
{ required: true, message: '请输入任务名称', trigger: 'blur' }
],
description: [
{ required: true, message: '请输入任务描述', trigger: 'blur' }
],
beanName: [
{ required: true, message: '请输入Bean名称', trigger: 'blur' }
],
methodName: [
{ required: true, message: '请输入方法名称', trigger: 'blur' }
],
cronExpression: [
{ required: true, message: '请输入Cron表达式', trigger: 'blur' }
],
personInCharge: [
{ required: true, message: '请输入负责人名称', trigger: 'blur' }
]
}
}
},
methods: {
// 执行
execute(id) {
crudJob.execution(id).then(res => {
this.crud.notify('执行成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
}).catch(err => {
console.log(err.response.data.message)
})
},
// 改变状态
updateStatus(id, status) {
if (status === '恢复') {
this.updateParams(id)
}
crudJob.updateIsPause(id).then(res => {
this.crud.toQuery()
this.crud.notify(status + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
}).catch(err => {
console.log(err.response.data.message)
})
},
updateParams(id) {
console.log(id)
},
delMethod(id) {
this.delLoading = true
crudJob.del([id]).then(() => {
this.delLoading = false
this.$refs[id].doClose()
this.crud.dleChangePage(1)
this.crud.delSuccessNotify()
this.crud.toQuery()
}).catch(() => {
this.delLoading = false
this.$refs[id].doClose()
})
},
// 显示日志
doLog() {
this.$refs.log.dialog = true
this.$refs.log.doInit()
},
checkboxT(row, rowIndex) {
return row.id !== 1
}
}
}
</script>

View File

@@ -0,0 +1,104 @@
<template>
<el-dialog :visible.sync="dialog" append-to-body title="执行日志" width="88%">
<!-- 搜索 -->
<div class="head-container">
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<el-select v-model="query.isSuccess" placeholder="日志状态" clearable size="small" class="filter-item" style="width: 110px" @change="toQuery">
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
<!-- 导出 -->
<div style="display: inline-block;">
<el-button
:loading="downloadLoading"
size="mini"
class="filter-item"
type="warning"
icon="el-icon-download"
@click="downloadMethod"
>导出</el-button>
</div>
</div>
<!--表格渲染-->
<el-table v-loading="loading" :data="data" style="width: 100%;margin-top: -10px;">
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
<el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
<el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
<el-table-column :show-overflow-tooltip="true" prop="params" width="120px" label="参数" />
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
<el-table-column prop="createTime" label="异常详情" width="110px">
<template slot-scope="scope">
<el-button v-show="scope.row.exceptionDetail" size="mini" type="text" @click="info(scope.row.exceptionDetail)">查看详情</el-button>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" prop="time" width="100px" label="耗时(毫秒)" />
<el-table-column align="center" prop="isSuccess" width="80px" label="状态">
<template slot-scope="scope">
<el-tag :type="scope.row.isSuccess ? 'success' : 'danger'">{{ scope.row.isSuccess ? '成功' : '失败' }}</el-tag>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期" />
</el-table>
<el-dialog :visible.sync="errorDialog" append-to-body title="异常详情" width="85%">
<pre>{{ errorInfo }}</pre>
</el-dialog>
<!--分页组件-->
<el-pagination
:total="total"
:current-page="page + 1"
:page-size="6"
style="margin-top:8px;"
layout="total, prev, pager, next"
@size-change="sizeChange"
@current-change="pageChange"
/>
</el-dialog>
</template>
<script>
import crud from '@/mixins/crud'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { DateRangePicker },
mixins: [crud],
data() {
return {
title: '任务日志',
errorInfo: '', errorDialog: false,
enabledTypeOptions: [
{ key: 'true', display_name: '成功' },
{ key: 'false', display_name: '失败' }
]
}
},
methods: {
doInit() {
this.$nextTick(() => {
this.init()
})
},
// 获取数据前设置好接口地址
beforeInit() {
this.url = 'api/jobs/logs'
this.size = 6
return true
},
// 异常详情
info(errorInfo) {
this.errorInfo = errorInfo
this.errorDialog = true
}
}
}
</script>
<style scoped>
.java.hljs{
color: #444;
background: #ffffff !important;
}
::v-deep .el-dialog__body{
padding: 0 20px 10px 20px !important;
}
</style>

View File

@@ -0,0 +1,221 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="5" style="margin-bottom: 10px">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>个人信息</span>
</div>
<div>
<div style="text-align: center">
<div class="el-upload">
<img :src="user.avatarName ? baseApi + '/avatar/' + user.avatarName : Avatar" title="点击上传头像" class="avatar" @click="toggleShow">
<myUpload
v-model="show"
:headers="headers"
:url="updateAvatarApi"
@crop-upload-success="cropUploadSuccess"
/>
</div>
</div>
<ul class="user-info">
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
<li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
<li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
<li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
<li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
<li>
<svg-icon icon-class="anq" /> 安全设置
<div class="user-right">
<a @click="$refs.pass.dialog = true">修改密码</a>
<a @click="$refs.email.dialog = true">修改邮箱</a>
</div>
</li>
</ul>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="19">
<!-- 用户资料 -->
<el-card class="box-card">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="用户资料" name="first">
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px">
<el-form-item label="昵称" prop="nickName">
<el-input v-model="form.nickName" style="width: 35%" />
<span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="form.phone" style="width: 35%;" />
<span style="color: #C0C0C0;margin-left: 10px;">手机号码不能重复</span>
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.gender" style="width: 178px">
<el-radio label="男"></el-radio>
<el-radio label="女"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="">
<el-button :loading="saveLoading" size="mini" type="primary" @click="doSubmit">保存配置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 操作日志 -->
<el-tab-pane label="操作日志" name="second">
<el-table v-loading="loading" :data="data" style="width: 100%;">
<el-table-column prop="description" label="行为" />
<el-table-column prop="requestIp" label="IP" />
<el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
<el-table-column prop="browser" label="浏览器" />
<el-table-column prop="time" label="请求耗时" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
<el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
<el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
</template>
</el-table-column>
<el-table-column
align="right"
>
<template slot="header">
<div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i class="el-icon-refresh" style="margin-left: 40px" /></div>
</template>
<template slot-scope="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<el-pagination
:total="total"
:current-page="page + 1"
style="margin-top: 8px;"
layout="total, prev, pager, next, sizes"
@size-change="sizeChange"
@current-change="pageChange"
/>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
<updateEmail ref="email" :email="user.email" />
<updatePass ref="pass" />
</div>
</template>
<script>
import myUpload from 'vue-image-crop-upload'
import { mapGetters } from 'vuex'
import updatePass from './center/updatePass'
import updateEmail from './center/updateEmail'
import { getToken } from '@/utils/auth'
import store from '@/store'
import { isvalidPhone } from '@/utils/validate'
import crud from '@/mixins/crud'
import { editUser } from '@/api/system/user'
import Avatar from '@/assets/images/avatar.png'
export default {
name: 'Center',
components: { updatePass, updateEmail, myUpload },
mixins: [crud],
data() {
// 自定义验证
const validPhone = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入电话号码'))
} else if (!isvalidPhone(value)) {
callback(new Error('请输入正确的11位手机号码'))
} else {
callback()
}
}
return {
show: false,
Avatar: Avatar,
activeName: 'first',
saveLoading: false,
headers: {
'Authorization': getToken()
},
form: {},
rules: {
nickName: [
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
],
phone: [
{ required: true, trigger: 'blur', validator: validPhone }
]
}
}
},
computed: {
...mapGetters([
'user',
'updateAvatarApi',
'baseApi'
])
},
created() {
this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
store.dispatch('GetInfo').then(() => {})
},
methods: {
toggleShow() {
this.show = !this.show
},
handleClick(tab, event) {
if (tab.name === 'second') {
this.init()
}
},
beforeInit() {
this.url = 'api/logs/user'
return true
},
cropUploadSuccess(jsonData, field) {
store.dispatch('GetInfo').then(() => {})
},
doSubmit() {
if (this.$refs['form']) {
this.$refs['form'].validate((valid) => {
if (valid) {
this.saveLoading = true
editUser(this.form).then(() => {
this.editSuccessNotify()
store.dispatch('GetInfo').then(() => {})
this.saveLoading = false
}).catch(() => {
this.saveLoading = false
})
}
})
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.avatar {
width: 120px;
height: 120px;
border-radius: 50%;
}
.user-info {
padding-left: 0;
list-style: none;
li{
border-bottom: 1px solid #F0F3F4;
padding: 11px 0;
font-size: 13px;
}
.user-right {
float: right;
a{
color: #317EF3;
}
}
}
</style>

View File

@@ -0,0 +1,137 @@
<template>
<div style="display: inline-block;">
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="475px" @close="cancel">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
<el-form-item label="新邮箱" prop="email">
<el-input v-model="form.email" auto-complete="on" style="width: 200px;" />
<el-button :loading="codeLoading" :disabled="isDisabled" size="small" @click="sendCode">{{ buttonName }}</el-button>
</el-form-item>
<el-form-item label="验证码" prop="code">
<el-input v-model="form.code" style="width: 320px;" />
</el-form-item>
<el-form-item label="当前密码" prop="pass">
<el-input v-model="form.pass" type="password" style="width: 320px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="cancel">取消</el-button>
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import store from '@/store'
import { validEmail } from '@/utils/validate'
import { updateEmail } from '@/api/system/user'
import { resetEmail } from '@/api/system/code'
export default {
props: {
email: {
type: String,
required: true
}
},
data() {
const validMail = (rule, value, callback) => {
if (value === '' || value === null) {
callback(new Error('新邮箱不能为空'))
} else if (value === this.email) {
callback(new Error('新邮箱不能与旧邮箱相同'))
} else if (validEmail(value)) {
callback()
} else {
callback(new Error('邮箱格式错误'))
}
}
return {
loading: false, dialog: false, title: '修改邮箱', form: { pass: '', email: '', code: '' },
user: { email: '', password: '' }, codeLoading: false,
buttonName: '获取验证码', isDisabled: false, time: 60,
rules: {
pass: [
{ required: true, message: '当前密码不能为空', trigger: 'blur' }
],
email: [
{ required: true, validator: validMail, trigger: 'blur' }
],
code: [
{ required: true, message: '验证码不能为空', trigger: 'blur' }
]
}
}
},
methods: {
cancel() {
this.resetForm()
},
sendCode() {
if (this.form.email && this.form.email !== this.email) {
this.codeLoading = true
this.buttonName = '验证码发送中'
const _this = this
resetEmail(this.form.email).then(res => {
this.$message({
showClose: true,
message: '发送成功验证码有效期5分钟',
type: 'success'
})
this.codeLoading = false
this.isDisabled = true
this.buttonName = this.time-- + '秒后重新发送'
this.timer = window.setInterval(function() {
_this.buttonName = _this.time + '秒后重新发送'
--_this.time
if (_this.time < 0) {
_this.buttonName = '重新发送'
_this.time = 60
_this.isDisabled = false
window.clearInterval(_this.timer)
}
}, 1000)
}).catch(err => {
this.resetForm()
this.codeLoading = false
console.log(err.response.data.message)
})
}
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
updateEmail(this.form).then(res => {
this.loading = false
this.resetForm()
this.$notify({
title: '邮箱修改成功',
type: 'success',
duration: 1500
})
store.dispatch('GetInfo').then(() => {})
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
},
resetForm() {
this.dialog = false
this.$refs['form'].resetFields()
window.clearInterval(this.timer)
this.time = 60
this.buttonName = '获取验证码'
this.isDisabled = false
this.form = { pass: '', email: '', code: '' }
}
}
}
</script>
<style scoped>
</style>

Some files were not shown because too many files have changed in this diff Show More