1
This commit is contained in:
119
src/api/index.js
119
src/api/index.js
@@ -25,14 +25,7 @@ export function EditContent(params) {
|
||||
export function exportAdminAll(params) {
|
||||
return request.post('/admin/admin_all', params)
|
||||
}
|
||||
// 用户权限修改
|
||||
export function PermissUpdate(params) {
|
||||
return request.post('/UpdatePermission', params)
|
||||
}
|
||||
// 用户权限读取
|
||||
export function PermissRead(params) {
|
||||
return request.post('/UpdatePermission', params)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单
|
||||
@@ -373,24 +366,74 @@ export function ShowAppointmentTime(params) {
|
||||
export function ModifyAppointmentTime(params) {
|
||||
return request.post('/ModifyAppointmentTime', params)
|
||||
}
|
||||
|
||||
// 活动
|
||||
/**
|
||||
* 活动发布
|
||||
*/
|
||||
// 活动列表
|
||||
export function getEventList(params) {
|
||||
return request.post('/eventList2', params)
|
||||
return request.get('/getEventList', { params })
|
||||
}
|
||||
// 活动新增
|
||||
export function addEvent(params) {
|
||||
return request.post('/addEvent', params)
|
||||
}
|
||||
// 活动详情
|
||||
export function getEvent(params) {
|
||||
return request.get('/getEvent', { params })
|
||||
}
|
||||
// 活动更新
|
||||
export function getEventUpdate(params) {
|
||||
return request.post('/getEventUpdate', params)
|
||||
}
|
||||
// 活动删除
|
||||
export function getEventDelete(params) {
|
||||
return request.post('/getEventDelete', params)
|
||||
}
|
||||
// 富文本上传
|
||||
export function upload(data) {
|
||||
return request.post('/upload', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
})
|
||||
}
|
||||
// 退款订单
|
||||
export function getRefundOrder(params) {
|
||||
return request.get('/getRefundOrder', { params })
|
||||
}
|
||||
// 权限
|
||||
// 用户权限修改
|
||||
export function PermissUpdate(params) {
|
||||
return request.post('/UpdatePermission', params)
|
||||
}
|
||||
// 用户权限读取
|
||||
export function PermissRead(params) {
|
||||
return request.post('/UpdatePermission', params)
|
||||
}
|
||||
// 获取角色列表
|
||||
export function getRoleList(params) {
|
||||
return request.get('/getRoleList', { params })
|
||||
}
|
||||
// 新增角色
|
||||
export function addRole(params) {
|
||||
return request.post('/addRole', params)
|
||||
}
|
||||
// 修改角色
|
||||
export function getRoleUpdate(params) {
|
||||
return request.post('/getRoleUpdate', params)
|
||||
}
|
||||
// 角色详情
|
||||
export function getRoleRead(params) {
|
||||
return request.get('/getRoleRead', { params })
|
||||
}
|
||||
// 删除角色
|
||||
export function getRoleDelete(params) {
|
||||
return request.post('/getRoleDelete', params)
|
||||
}
|
||||
// 权限
|
||||
export function GetRole(params) {
|
||||
return request.get('/getRoleList', { params })
|
||||
}
|
||||
// 权限
|
||||
// 菜单
|
||||
export function getMenuList(params) {
|
||||
return request.get('/getMenuList', { params })
|
||||
}
|
||||
@@ -455,6 +498,52 @@ export function getUserMoneyLog(params) {
|
||||
export function orderTotal(params) {
|
||||
return request.get('/orderTotal', { params })
|
||||
}
|
||||
/**
|
||||
* 优惠券
|
||||
*/
|
||||
// 优惠券列表
|
||||
export function getCouponList(params) {
|
||||
return request.get('/getCouponList', { params })
|
||||
}
|
||||
// 新增优惠券
|
||||
export function addCoupon(params) {
|
||||
return request.post('/addCoupon', params)
|
||||
}
|
||||
// 优惠券详情
|
||||
export function getCoupon(params) {
|
||||
return request.get('/getCoupon', { params })
|
||||
}
|
||||
// 更新优惠券
|
||||
export function getCouponUpdate(params) {
|
||||
return request.post('/getCouponUpdate', params)
|
||||
}
|
||||
// 删除优惠券
|
||||
export function getCouponDelete(params) {
|
||||
return request.post('/getCouponDelete', params)
|
||||
}
|
||||
/**
|
||||
* 会员
|
||||
*/
|
||||
// 会员列表
|
||||
export function getVipList(params) {
|
||||
return request.get('/getVipList', { params })
|
||||
}
|
||||
// 新增会员
|
||||
export function addVip(params) {
|
||||
return request.post('/addVip', params)
|
||||
}
|
||||
// 会员详情
|
||||
export function getVip(params) {
|
||||
return request.get('/getVip', { params })
|
||||
}
|
||||
// 更新会员
|
||||
export function getVipUpdate(params) {
|
||||
return request.post('/getVipUpdate', params)
|
||||
}
|
||||
// 删除会员
|
||||
export function getVipDelete(params) {
|
||||
return request.post('/getVipDelete', params)
|
||||
}
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@@ -478,3 +567,7 @@ export function getAdminUpdate(params) {
|
||||
export function getAdminDelete(params) {
|
||||
return request.post('/getAdminDelete', params)
|
||||
}
|
||||
// 充电桩分析
|
||||
export function getChargePile(params) {
|
||||
return request.get('/getChargePile', params)
|
||||
}
|
||||
280
src/components/RichEditor.vue
Normal file
280
src/components/RichEditor.vue
Normal file
@@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<div class="rich-editor">
|
||||
<div class="rich-editor__toolbar">
|
||||
<ButtonGroup size="small" class="rich-editor__toolbar-group">
|
||||
<Button
|
||||
v-for="btn in toolbarButtons"
|
||||
:key="btn.key"
|
||||
:title="btn.label"
|
||||
:icon="btn.icon"
|
||||
@click="handleToolbar(btn)"
|
||||
>
|
||||
{{ btn.label }}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Button size="small" icon="ios-image" type="primary" class="rich-editor__upload" :loading="uploading"
|
||||
@click="triggerUpload">
|
||||
插入图片
|
||||
</Button>
|
||||
<input ref="fileInput" class="rich-editor__file" type="file" accept="image/*" @change="handleUpload">
|
||||
</div>
|
||||
<div ref="editor" class="rich-editor__content" :data-placeholder="placeholder" :style="{ minHeight: `${height}px` }"
|
||||
contenteditable
|
||||
@input="handleInput" @focus="saveSelection" @keyup="saveSelection" @mouseup="saveSelection" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { upload } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'RichEditor',
|
||||
props: {
|
||||
// 富文本初始内容
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 提示语,用于空状态展示
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入内容',
|
||||
},
|
||||
// 文本区域高度
|
||||
height: {
|
||||
type: Number,
|
||||
default: 280,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
innerHTML: this.value,
|
||||
currentRange: null,
|
||||
uploading: false,
|
||||
toolbarButtons: [{
|
||||
key: 'bold',
|
||||
icon: 'ios-bold',
|
||||
label: '加粗',
|
||||
command: 'bold',
|
||||
},
|
||||
{
|
||||
key: 'italic',
|
||||
icon: 'ios-italic',
|
||||
label: '斜体',
|
||||
command: 'italic',
|
||||
},
|
||||
{
|
||||
key: 'underline',
|
||||
icon: 'ios-underline',
|
||||
label: '下划线',
|
||||
command: 'underline',
|
||||
},
|
||||
// 段落排版按钮:左/中/右对齐与分割线,覆盖常用排版需求
|
||||
{
|
||||
key: 'align-left',
|
||||
icon: 'ios-arrow-back',
|
||||
label: '左对齐',
|
||||
command: 'justifyLeft',
|
||||
},
|
||||
{
|
||||
key: 'align-center',
|
||||
icon: 'ios-remove',
|
||||
label: '居中对齐',
|
||||
command: 'justifyCenter',
|
||||
},
|
||||
{
|
||||
key: 'align-right',
|
||||
icon: 'ios-arrow-forward',
|
||||
label: '右对齐',
|
||||
command: 'justifyRight',
|
||||
},
|
||||
{
|
||||
key: 'horizontal-rule',
|
||||
icon: 'md-more',
|
||||
label: '分割线',
|
||||
command: 'insertHorizontalRule',
|
||||
},
|
||||
{
|
||||
key: 'clear',
|
||||
icon: 'md-trash',
|
||||
label: '清空内容',
|
||||
action: 'clear',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (val !== this.innerHTML) {
|
||||
this.innerHTML = val || ''
|
||||
this.setContent(this.innerHTML)
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setContent(this.innerHTML)
|
||||
},
|
||||
methods: {
|
||||
// 将外部内容同步到编辑器
|
||||
setContent(content) {
|
||||
if (this.$refs.editor) {
|
||||
this.$refs.editor.innerHTML = content || ''
|
||||
}
|
||||
},
|
||||
// 执行基础格式化命令
|
||||
execCommand(command) {
|
||||
this.focusEditor()
|
||||
document.execCommand(command, false, null)
|
||||
this.syncContent()
|
||||
},
|
||||
// 点击工具栏按钮时统一处理,支持命令或自定义方法
|
||||
handleToolbar(btn) {
|
||||
if (btn.action === 'clear') {
|
||||
this.clearContent()
|
||||
return
|
||||
}
|
||||
if (btn.command) {
|
||||
this.execCommand(btn.command)
|
||||
}
|
||||
},
|
||||
// 清空内容
|
||||
clearContent() {
|
||||
this.setContent('')
|
||||
this.syncContent()
|
||||
},
|
||||
// 编辑区域获得焦点,方便触发格式化命令
|
||||
focusEditor() {
|
||||
if (this.$refs.editor) {
|
||||
this.$refs.editor.focus()
|
||||
this.restoreSelection()
|
||||
}
|
||||
},
|
||||
// 监听输入事件,将HTML回写给父组件
|
||||
handleInput() {
|
||||
this.syncContent()
|
||||
},
|
||||
// 记录当前的光标选区,便于插入图片或文本
|
||||
saveSelection() {
|
||||
const selection = window.getSelection()
|
||||
if (selection.rangeCount > 0) {
|
||||
this.currentRange = selection.getRangeAt(0)
|
||||
}
|
||||
},
|
||||
// 当外部操作打断焦点时,恢复光标
|
||||
restoreSelection() {
|
||||
if (!this.currentRange) {
|
||||
return
|
||||
}
|
||||
const selection = window.getSelection()
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(this.currentRange)
|
||||
},
|
||||
// 同步内容到父组件
|
||||
syncContent() {
|
||||
if (!this.$refs.editor) {
|
||||
return
|
||||
}
|
||||
this.innerHTML = this.$refs.editor.innerHTML
|
||||
this.$emit('input', this.innerHTML)
|
||||
this.$emit('change', this.innerHTML)
|
||||
},
|
||||
// 点击上传按钮
|
||||
triggerUpload() {
|
||||
if (this.uploading) {
|
||||
return
|
||||
}
|
||||
this.$refs.fileInput.value = ''
|
||||
this.$refs.fileInput.click()
|
||||
},
|
||||
// 选择图片后上传,并插入富文本
|
||||
async handleUpload(event) {
|
||||
const file = event.target.files && event.target.files[0]
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
await this.uploadImage(file)
|
||||
event.target.value = ''
|
||||
},
|
||||
// 将图片上传到后端
|
||||
async uploadImage(file) {
|
||||
const isImage = file.type && file.type.indexOf('image/') === 0
|
||||
if (!isImage) {
|
||||
this.$Message.error('仅支持上传图片文件')
|
||||
return
|
||||
}
|
||||
this.uploading = true
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const res = await upload(formData)
|
||||
const fileUrl = res.data
|
||||
if (!fileUrl) {
|
||||
throw new Error('上传接口未返回图片地址')
|
||||
}
|
||||
this.insertImage(fileUrl)
|
||||
this.$Message.success('图片上传成功')
|
||||
} catch (error) {
|
||||
this.$Message.error(error.msg || '图片上传失败,请稍后重试')
|
||||
} finally {
|
||||
this.uploading = false
|
||||
}
|
||||
},
|
||||
// 插入图片到当前光标位置
|
||||
insertImage(url) {
|
||||
this.focusEditor()
|
||||
document.execCommand('insertImage', false, url)
|
||||
this.syncContent()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.rich-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rich-editor__toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.rich-editor__content {
|
||||
height: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #dcdee2;
|
||||
border-radius: 8px;
|
||||
line-height: 1.6;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.rich-editor__content:focus {
|
||||
outline: none;
|
||||
border-color: #2d8cf0;
|
||||
box-shadow: 0 0 0 2px rgba(45, 140, 240, 0.1);
|
||||
}
|
||||
|
||||
.rich-editor__content:empty:before {
|
||||
content: attr(data-placeholder);
|
||||
color: #c5c8ce;
|
||||
}
|
||||
|
||||
.rich-editor__upload {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.rich-editor__toolbar-group .ivu-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.rich-editor__toolbar-group .ivu-btn .ivu-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.rich-editor__file {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@ import router from './router'
|
||||
import store from './store'
|
||||
import createRoutes from '@/utils/createRoutes'
|
||||
import { getDocumentTitle, resetTokenAndClearUser } from './utils'
|
||||
|
||||
// import { getMenuList } from './api'
|
||||
// 是否有菜单数据
|
||||
let hasMenus = false
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
@@ -16,10 +16,22 @@ router.beforeEach(async (to, from, next) => {
|
||||
next()
|
||||
} else {
|
||||
try {
|
||||
let menuItems = store.state.menuItems
|
||||
// let apiMenu = []
|
||||
// await getMenuList({ token: localStorage.getItem('token') }).then((res) => {
|
||||
// res.forEach((i) => {
|
||||
// apiMenu.push({
|
||||
// text: i.text,
|
||||
// type: i.type,
|
||||
// children: i.children,
|
||||
// })
|
||||
// })
|
||||
// menuItems = apiMenu
|
||||
// })
|
||||
// 这里可以用 await 配合请求后台数据来生成路由
|
||||
// const data = await axios.get('xxx')
|
||||
// const routes = createRoutes(data)
|
||||
const routes = createRoutes(store.state.menuItems)
|
||||
const routes = createRoutes(menuItems)
|
||||
// 动态添加路由
|
||||
router.addRoutes(routes)
|
||||
hasMenus = true
|
||||
|
||||
@@ -67,11 +67,11 @@ const store = new Vuex.Store({
|
||||
// url: 'https://www.baidu.com',
|
||||
// isExternal: true, // 外链 跳到一个外部的 URL 页面
|
||||
},
|
||||
{
|
||||
size: 18,
|
||||
text: '钱包账户',
|
||||
type: 'ios-paper',
|
||||
},
|
||||
// {
|
||||
// size: 18,
|
||||
// text: '钱包账户',
|
||||
// type: 'ios-paper',
|
||||
// },
|
||||
{
|
||||
size: 18,
|
||||
text: '车辆管理',
|
||||
|
||||
@@ -1,261 +1,413 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem prop="user" label="充电桩名称:">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit('formInline')">搜索</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="action-btn">
|
||||
<Button type="primary" @click="show_modal = true">添加</Button>
|
||||
<!-- <Button type="error">删除</Button> -->
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data">
|
||||
<template #action="{ row, index }">
|
||||
<Button type="primary" size="small" style="margin-right: 5px" @click="show(index)">编辑</Button>
|
||||
<Button type="error" size="small" @click="remove(index)">删除</Button>
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" show-total show-sizer class="page" />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 添加、编辑 -->
|
||||
<Modal v-model="show_modal" title="Common Modal dialog box title" :mask-closable="false" @on-ok="ok"
|
||||
@on-cancel="cancel">
|
||||
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="80">
|
||||
<FormItem label="Name" prop="name">
|
||||
<Input v-model="formValidate.name" placeholder="Enter your name" />
|
||||
</FormItem>
|
||||
<FormItem label="E-mail" prop="mail">
|
||||
<Input v-model="formValidate.mail" placeholder="Enter your e-mail" />
|
||||
</FormItem>
|
||||
<FormItem label="City" prop="city">
|
||||
<Select v-model="formValidate.city" placeholder="Select your city">
|
||||
<Option value="beijing">New York</Option>
|
||||
<Option value="shanghai">London</Option>
|
||||
<Option value="shenzhen">Sydney</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="Date">
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<DatePicker type="date" placeholder="Select date" v-model="formValidate.date"></DatePicker>
|
||||
</Col>
|
||||
<Col span="2" style="text-align: center">-</Col>
|
||||
<Col span="11">
|
||||
<TimePicker type="time" placeholder="Select time" v-model="formValidate.time"></TimePicker>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
<FormItem label="Gender" prop="gender">
|
||||
<RadioGroup v-model="formValidate.gender">
|
||||
<Radio label="male">Male</Radio>
|
||||
<Radio label="female">Female</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem label="Hobby" prop="interest">
|
||||
<CheckboxGroup v-model="formValidate.interest">
|
||||
<Checkbox label="Eat"></Checkbox>
|
||||
<Checkbox label="Sleep"></Checkbox>
|
||||
<Checkbox label="Run"></Checkbox>
|
||||
<Checkbox label="Movie"></Checkbox>
|
||||
</CheckboxGroup>
|
||||
</FormItem>
|
||||
<FormItem label="Desc" prop="desc">
|
||||
<Input v-model="formValidate.desc" type="textarea" :autosize="{ minRows: 2, maxRows: 5 }"
|
||||
placeholder="Enter something..." />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit2('formValidate')">Submit</Button>
|
||||
<Button @click="handleReset('formValidate')" style="margin-left: 8px">Reset</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem label="活动标题:" prop="title">
|
||||
<Input type="text" clearable v-model="formInline.title" placeholder="请输入活动标题" />
|
||||
</FormItem>
|
||||
<FormItem label="状态:" prop="status">
|
||||
<Select v-model="formInline.status" clearable placeholder="全部状态" style="width: 200px">
|
||||
<Option v-for="item in statusOptions" :key="item.value" :value="item.value">
|
||||
{{ item.label }}
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit">搜索</Button>
|
||||
<Button style="margin-left: 8px" @click="resetSearch">重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="action-btn">
|
||||
<Button type="primary" @click="add">添加</Button>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data" :loading="loading">
|
||||
<template #status="{ row }">
|
||||
{{ getStatusText(row.status) }}
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Button type="primary" size="small" style="margin-right: 5px" @click="show(row.id)">编辑</Button>
|
||||
<Button type="error" size="small" @click="remove(row.id)">删除</Button>
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" show-total show-sizer class="page" :current="page" :page-size="pageSize"
|
||||
@on-change="onChangePage" @on-page-size-change="onChangePageSize" />
|
||||
</div>
|
||||
<Modal v-model="show_modal" :title="modalTitle" :mask-closable="false" :width="820" @on-cancel="cancel">
|
||||
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="100">
|
||||
<FormItem label="活动标题" prop="title">
|
||||
<Input v-model="formValidate.title" placeholder="请输入活动标题" />
|
||||
</FormItem>
|
||||
<FormItem label="活动状态" prop="status">
|
||||
<RadioGroup v-model="formValidate.status">
|
||||
<Radio label="1">已发布</Radio>
|
||||
<Radio label="0">未发布</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem label="活动内容" prop="content">
|
||||
<RichEditor v-model="formValidate.content" placeholder="请输入活动内容,支持图片上传" :height="320"
|
||||
@change="handleContentChange" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div slot="footer">
|
||||
<Button type="text" @click="cancel">取消</Button>
|
||||
<Button type="primary" :loading="submitLoading" @click="ok">确定</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getEventList } from '@/api'
|
||||
import {
|
||||
getEventList,
|
||||
addEvent,
|
||||
getEvent,
|
||||
getEventUpdate,
|
||||
getEventDelete,
|
||||
} from '@/api'
|
||||
import RichEditor from '@/components/RichEditor.vue'
|
||||
|
||||
export default {
|
||||
name: 'charging_station',
|
||||
data() {
|
||||
return {
|
||||
show_modal: false,
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
formInline: {
|
||||
user: '',
|
||||
password: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [
|
||||
{
|
||||
title: '活动名称',
|
||||
key: 'title',
|
||||
},
|
||||
{
|
||||
title: '活动状态',
|
||||
key: 'statusMsg',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'create_time',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
formValidate: {
|
||||
name: '',
|
||||
mail: '',
|
||||
city: '',
|
||||
gender: '',
|
||||
interest: [],
|
||||
date: '',
|
||||
time: '',
|
||||
desc: '',
|
||||
},
|
||||
ruleValidate: {
|
||||
name: [
|
||||
{ required: true, message: 'The name cannot be empty', trigger: 'blur' },
|
||||
],
|
||||
mail: [
|
||||
{ required: true, message: 'Mailbox cannot be empty', trigger: 'blur' },
|
||||
{ type: 'email', message: 'Incorrect email format', trigger: 'blur' },
|
||||
],
|
||||
city: [
|
||||
{ required: true, message: 'Please select the city', trigger: 'change' },
|
||||
],
|
||||
gender: [
|
||||
{ required: true, message: 'Please select gender', trigger: 'change' },
|
||||
],
|
||||
interest: [
|
||||
{ required: true, type: 'array', min: 1, message: 'Choose at least one hobby', trigger: 'change' },
|
||||
{ type: 'array', max: 2, message: 'Choose two hobbies at best', trigger: 'change' },
|
||||
],
|
||||
date: [
|
||||
{ required: true, type: 'date', message: 'Please select the date', trigger: 'change' },
|
||||
],
|
||||
time: [
|
||||
{ required: true, type: 'string', message: 'Please select time', trigger: 'change' },
|
||||
],
|
||||
desc: [
|
||||
{ required: true, message: 'Please enter a personal introduction', trigger: 'blur' },
|
||||
{ type: 'string', min: 20, message: 'Introduce no less than 20 words', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
this.calculateTableHeight()
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
methods: {
|
||||
calculateTableHeight() {
|
||||
// 计算表格高度 = 窗口高度 - 搜索区域高度 - 分页高度 - 其他间距
|
||||
const searchHeight = document.querySelector('.search-area').offsetHeight
|
||||
const actionBtnHeight = document.querySelector('.action-btn').offsetHeight
|
||||
const pageHeight = 32 // 分页组件大约高度
|
||||
const margins = 40 // 上下边距总和
|
||||
this.tableHeight = window.innerHeight - actionBtnHeight - searchHeight - pageHeight - margins - 130
|
||||
},
|
||||
async getList() {
|
||||
await getEventList({ page: 1 }).then((res) => {
|
||||
this.total = res.total
|
||||
this.data = res.data
|
||||
this.page = res.current_page
|
||||
})
|
||||
},
|
||||
handleSubmit(name) {
|
||||
this.$refs[name].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$Message.success('Success!')
|
||||
} else {
|
||||
this.$Message.error('Fail!')
|
||||
}
|
||||
})
|
||||
},
|
||||
handleReset(name) {
|
||||
this.$refs[name].resetFields()
|
||||
},
|
||||
handleSubmit2(name) {
|
||||
this.$refs[name].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$Message.success('Success!')
|
||||
} else {
|
||||
this.$Message.error('Fail!')
|
||||
}
|
||||
})
|
||||
},
|
||||
show(index) {
|
||||
this.$Modal.info({
|
||||
title: 'User Info',
|
||||
content: `Name:${this.data[index].name}<br>Age:${this.data[index].age}<br>Address:${this.data[index].address}`,
|
||||
})
|
||||
},
|
||||
remove(index) {
|
||||
this.data.splice(index, 1)
|
||||
},
|
||||
ok() {
|
||||
this.$Message.info('Clicked ok')
|
||||
},
|
||||
cancel() {
|
||||
this.$Message.info('Clicked cancel')
|
||||
},
|
||||
},
|
||||
}
|
||||
export default {
|
||||
name: 'activity',
|
||||
components: {
|
||||
RichEditor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false, // 列表查询 loading 态
|
||||
show_modal: false, // 新增/编辑弹窗显隐
|
||||
modalTitle: '添加活动', // 弹窗标题
|
||||
page: 1, // 当前页码
|
||||
pageSize: 10, // 每页数量
|
||||
total: 0, // 数据总条数
|
||||
tableHeight: 500, // 表格高度,用于自适应
|
||||
formInline: {
|
||||
title: '',
|
||||
status: '',
|
||||
},
|
||||
ruleInline: {}, // 站点管理页面无搜索校验,此处保持空对象占位
|
||||
statusOptions: [{
|
||||
label: '全部状态',
|
||||
value: '', // 传空值表示不筛选状态
|
||||
},
|
||||
{
|
||||
label: '未发布',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
label: '已发布',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
statusMap: {
|
||||
0: '未发布',
|
||||
1: '已发布',
|
||||
},
|
||||
columns: [{ // 表格列配置,沿用站点管理页面 Table 风格
|
||||
title: '活动标题',
|
||||
key: 'title',
|
||||
},
|
||||
{
|
||||
title: '活动状态',
|
||||
slot: 'status',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'create_time',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
data: [], // 活动列表数据源
|
||||
formValidate: {
|
||||
id: '', // 活动主键,编辑时使用
|
||||
title: '', // 活动标题
|
||||
status: '1', // 活动状态:1 已发布,0 未发布
|
||||
content: '', // 富文本正文
|
||||
},
|
||||
ruleValidate: {
|
||||
title: [{
|
||||
required: true,
|
||||
message: '请输入活动标题',
|
||||
trigger: 'blur',
|
||||
}],
|
||||
status: [{
|
||||
required: true,
|
||||
message: '请选择活动状态',
|
||||
trigger: 'change',
|
||||
}],
|
||||
content: [{
|
||||
required: true,
|
||||
message: '请输入活动内容',
|
||||
trigger: 'change',
|
||||
}],
|
||||
},
|
||||
submitLoading: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
this.calculateTableHeight()
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
methods: {
|
||||
// 自适应计算表格高度,保持页面视觉与站点管理页面一致
|
||||
calculateTableHeight() {
|
||||
const search = document.querySelector('.search-area')
|
||||
const action = document.querySelector('.action-btn')
|
||||
const searchHeight = search ? search.offsetHeight : 0
|
||||
const actionHeight = action ? action.offsetHeight : 0
|
||||
const pageHeight = 32
|
||||
const margins = 40
|
||||
this.tableHeight = window.innerHeight - searchHeight - actionHeight - pageHeight - margins - 130
|
||||
if (this.tableHeight < 300) {
|
||||
this.tableHeight = 300
|
||||
}
|
||||
},
|
||||
// 获取活动列表数据
|
||||
async getList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const params = {
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
title: this.formInline.title,
|
||||
status: this.formInline.status,
|
||||
}
|
||||
if (params.status === '') {
|
||||
delete params.status
|
||||
}
|
||||
const res = await getEventList(params)
|
||||
// 兼容后端返回 { data: { list: [], total: 0 } } 或 { data: [], total: 0 } 的场景
|
||||
const responseData = res.data || res || {}
|
||||
let list = []
|
||||
if (Array.isArray(responseData)) {
|
||||
list = responseData
|
||||
this.total = responseData.length
|
||||
} else if (responseData && typeof responseData === 'object') {
|
||||
list = responseData.data || responseData.list || responseData.records || []
|
||||
const total = responseData.total || responseData.count || responseData.totalCount
|
||||
if (typeof total === 'number') {
|
||||
this.total = total
|
||||
} else if (Array.isArray(list)) {
|
||||
this.total = list.length
|
||||
} else {
|
||||
this.total = 0
|
||||
}
|
||||
if (responseData.current_page) {
|
||||
this.page = responseData.current_page
|
||||
}
|
||||
}
|
||||
this.data = Array.isArray(list) ? list : []
|
||||
} catch (error) {
|
||||
this.data = []
|
||||
this.total = 0
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
// 点击搜索按钮,重置页码并刷新列表
|
||||
handleSubmit() {
|
||||
this.page = 1
|
||||
this.getList()
|
||||
},
|
||||
// 重置查询条件,保持行为与站点管理搜索一致
|
||||
resetSearch() {
|
||||
this.formInline.title = ''
|
||||
this.formInline.status = ''
|
||||
if (this.$refs.formInline) {
|
||||
this.$refs.formInline.resetFields()
|
||||
}
|
||||
this.handleSubmit()
|
||||
},
|
||||
// 新增活动
|
||||
add() {
|
||||
this.modalTitle = '添加活动'
|
||||
this.show_modal = true
|
||||
this.resetForm()
|
||||
},
|
||||
// 重置弹窗表单字段
|
||||
resetForm() {
|
||||
Object.assign(this.formValidate, {
|
||||
id: '',
|
||||
title: '',
|
||||
status: '1',
|
||||
content: '',
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.resetFields()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 取消弹窗
|
||||
cancel() {
|
||||
this.show_modal = false
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.resetFields()
|
||||
}
|
||||
},
|
||||
// 点击编辑时加载活动详情
|
||||
async show(id) {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getEvent({
|
||||
id,
|
||||
})
|
||||
const info = res.data || {}
|
||||
Object.assign(this.formValidate, {
|
||||
id: info.id,
|
||||
title: info.title || '',
|
||||
status: info.status != null ? String(info.status) : '0',
|
||||
content: info.content || '',
|
||||
})
|
||||
this.modalTitle = '编辑活动'
|
||||
this.show_modal = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.clearValidate()
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
this.$Message.error('获取活动详情失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
// 删除活动前二次确认
|
||||
async remove(id) {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
const confirm = await new Promise(resolve => {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '删除后不可恢复,确定继续吗?',
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => resolve(false),
|
||||
})
|
||||
})
|
||||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
await getEventDelete({
|
||||
id,
|
||||
})
|
||||
this.$Message.success('删除成功')
|
||||
if (this.data.length === 1 && this.page > 1) {
|
||||
this.page -= 1
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
// 新增/编辑提交保存
|
||||
async ok() {
|
||||
const valid = await new Promise(resolve => this.$refs.formValidate.validate(resolve))
|
||||
if (!valid) {
|
||||
this.$Message.error('请完善活动信息')
|
||||
return
|
||||
}
|
||||
this.submitLoading = true
|
||||
const payload = {
|
||||
id: this.formValidate.id,
|
||||
title: this.formValidate.title.trim(),
|
||||
status: Number(this.formValidate.status),
|
||||
content: this.formValidate.content,
|
||||
}
|
||||
try {
|
||||
if (payload.id) {
|
||||
await getEventUpdate(payload)
|
||||
this.$Message.success('活动更新成功')
|
||||
} else {
|
||||
await addEvent(payload)
|
||||
this.$Message.success('活动创建成功')
|
||||
}
|
||||
this.cancel()
|
||||
this.getList()
|
||||
} catch (error) {
|
||||
this.$Message.error('保存失败,请稍后重试')
|
||||
} finally {
|
||||
this.submitLoading = false
|
||||
}
|
||||
},
|
||||
// 分页切换
|
||||
onChangePage(e) {
|
||||
if (this.page !== e) {
|
||||
this.page = e
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
// pageSize切换
|
||||
onChangePageSize(e) {
|
||||
if (this.pageSize !== e) {
|
||||
this.page = 1
|
||||
this.pageSize = e
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
// 状态文案
|
||||
getStatusText(status) {
|
||||
const key = Number(status)
|
||||
return this.statusMap[key] || '未发布'
|
||||
},
|
||||
// 状态颜色
|
||||
getStatusColor(status) {
|
||||
return Number(status) === 1 ? 'success' : 'default'
|
||||
},
|
||||
// 富文本内容变更时主动触发校验,防止用户未输入内容直接提交
|
||||
handleContentChange() {
|
||||
if (this.show_modal && this.$refs.formValidate) {
|
||||
this.$refs.formValidate.validateField('content')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
margin: 20px 0;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
.action-btn {
|
||||
margin: 20px 0;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.action-btn button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
.search-area{
|
||||
background-color: white;
|
||||
padding-top: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.table-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.search-area {
|
||||
background-color: white;
|
||||
padding-top: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,261 +1,608 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem prop="user" label="优惠券名称:">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit('formInline')">搜索</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="action-btn">
|
||||
<Button type="primary" @click="show_modal = true">添加</Button>
|
||||
<!-- <Button type="error">删除</Button> -->
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data">
|
||||
<template #action="{ row, index }">
|
||||
<Button type="primary" size="small" style="margin-right: 5px" @click="show(index)">编辑</Button>
|
||||
<Button type="error" size="small" @click="remove(index)">删除</Button>
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" show-total show-sizer class="page" />
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem label="优惠券名称:" prop="name">
|
||||
<Input type="text" clearable v-model="formInline.name" placeholder="请输入优惠券名称" />
|
||||
</FormItem>
|
||||
<FormItem label="状态:" prop="status">
|
||||
<Select v-model="formInline.status" clearable placeholder="全部状态" style="width: 200px">
|
||||
<Option v-for="option in statusOptions" :key="`search-status-${option.value}`" :value="option.value">
|
||||
{{ option.label }}
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit">搜索</Button>
|
||||
<Button style="margin-left: 8px" @click="handleReset">重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="action-btn">
|
||||
<Button type="primary" @click="add">添加优惠券</Button>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data" :loading="tableLoading">
|
||||
<template #station="{ row }">
|
||||
<span>{{ row.stationLabel }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Button type="primary" size="small" style="margin-right: 5px" @click="show(row.id)">编辑</Button>
|
||||
<Button type="error" size="small" @click="remove(row.id)">删除</Button>
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" :current="page" :page-size="pageSize" show-total show-sizer class="page"
|
||||
@on-change="onChangePage" @on-page-size-change="onChangePageSize" />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 添加、编辑 -->
|
||||
<Modal v-model="show_modal" title="Common Modal dialog box title" :mask-closable="false" @on-ok="ok"
|
||||
@on-cancel="cancel">
|
||||
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="80">
|
||||
<FormItem label="Name" prop="name">
|
||||
<Input v-model="formValidate.name" placeholder="Enter your name" />
|
||||
</FormItem>
|
||||
<FormItem label="E-mail" prop="mail">
|
||||
<Input v-model="formValidate.mail" placeholder="Enter your e-mail" />
|
||||
</FormItem>
|
||||
<FormItem label="City" prop="city">
|
||||
<Select v-model="formValidate.city" placeholder="Select your city">
|
||||
<Option value="beijing">New York</Option>
|
||||
<Option value="shanghai">London</Option>
|
||||
<Option value="shenzhen">Sydney</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="Date">
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<DatePicker type="date" placeholder="Select date" v-model="formValidate.date"></DatePicker>
|
||||
</Col>
|
||||
<Col span="2" style="text-align: center">-</Col>
|
||||
<Col span="11">
|
||||
<TimePicker type="time" placeholder="Select time" v-model="formValidate.time"></TimePicker>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
<FormItem label="Gender" prop="gender">
|
||||
<RadioGroup v-model="formValidate.gender">
|
||||
<Radio label="male">Male</Radio>
|
||||
<Radio label="female">Female</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem label="Hobby" prop="interest">
|
||||
<CheckboxGroup v-model="formValidate.interest">
|
||||
<Checkbox label="Eat"></Checkbox>
|
||||
<Checkbox label="Sleep"></Checkbox>
|
||||
<Checkbox label="Run"></Checkbox>
|
||||
<Checkbox label="Movie"></Checkbox>
|
||||
</CheckboxGroup>
|
||||
</FormItem>
|
||||
<FormItem label="Desc" prop="desc">
|
||||
<Input v-model="formValidate.desc" type="textarea" :autosize="{ minRows: 2, maxRows: 5 }"
|
||||
placeholder="Enter something..." />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit2('formValidate')">Submit</Button>
|
||||
<Button @click="handleReset('formValidate')" style="margin-left: 8px">Reset</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
<Modal v-model="show_modal" :title="formValidate.id ? '编辑优惠券' : '添加优惠券'" :mask-closable="false"
|
||||
:closable="false" @on-cancel="cancel">
|
||||
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="120">
|
||||
<FormItem label="优惠券名称" prop="name">
|
||||
<Input v-model="formValidate.name" placeholder="请输入优惠券名称" />
|
||||
</FormItem>
|
||||
<FormItem label="适用站点" prop="station_id">
|
||||
<Select v-model="formValidate.station_id" placeholder="请选择关联站点" filterable style="width: 240px">
|
||||
<Option v-for="station in stationOptions" :key="station.charge_station_id"
|
||||
:value="station.charge_station_id">
|
||||
{{ station.charge_station_name }}
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="用户类型" prop="user_type">
|
||||
<RadioGroup v-model="formValidate.user_type">
|
||||
<Radio v-for="option in userTypeOptions" :key="`user-${option.value}`" :label="option.value">
|
||||
{{ option.label }}
|
||||
</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem label="使用门槛(元)" prop="threshold">
|
||||
<InputNumber v-model="formValidate.threshold" :min="0" :precision="2" :step="1" style="width: 240px" />
|
||||
</FormItem>
|
||||
<FormItem label="优惠金额(元)" prop="value">
|
||||
<InputNumber v-model="formValidate.value" :min="0" :precision="2" :step="1" style="width: 240px" />
|
||||
</FormItem>
|
||||
<FormItem label="过期时间" prop="expiration_time">
|
||||
<DatePicker type="datetime" format="yyyy-MM-dd HH:mm" v-model="formValidate.expiration_time"
|
||||
placeholder="请选择过期时间" style="width: 240px" />
|
||||
</FormItem>
|
||||
<FormItem label="状态" prop="status">
|
||||
<RadioGroup v-model="formValidate.status">
|
||||
<Radio v-for="option in statusOptions" :key="`status-${option.value}`" :label="option.value">
|
||||
{{ option.label }}
|
||||
</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div slot="footer">
|
||||
<Button type="text" @click="cancel">取消</Button>
|
||||
<Button type="primary" :loading="submitLoading" @click="ok">确定</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getEventList } from '@/api'
|
||||
import {
|
||||
getCouponList,
|
||||
addCoupon,
|
||||
getCoupon,
|
||||
getCouponUpdate,
|
||||
getCouponDelete,
|
||||
getStationList,
|
||||
} from '@/api'
|
||||
|
||||
const createDefaultCouponForm = () => ({
|
||||
id: '',
|
||||
name: '',
|
||||
station_id: '',
|
||||
user_type: 1,
|
||||
threshold: 0,
|
||||
value: 0,
|
||||
expiration_time: '',
|
||||
status: 1,
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'charging_station',
|
||||
data() {
|
||||
return {
|
||||
show_modal: false,
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
formInline: {
|
||||
user: '',
|
||||
password: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [
|
||||
{
|
||||
title: '优惠券名称',
|
||||
key: 'title',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'statusMsg',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'create_time',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
formValidate: {
|
||||
name: '',
|
||||
mail: '',
|
||||
city: '',
|
||||
gender: '',
|
||||
interest: [],
|
||||
date: '',
|
||||
time: '',
|
||||
desc: '',
|
||||
},
|
||||
ruleValidate: {
|
||||
name: [
|
||||
{ required: true, message: 'The name cannot be empty', trigger: 'blur' },
|
||||
],
|
||||
mail: [
|
||||
{ required: true, message: 'Mailbox cannot be empty', trigger: 'blur' },
|
||||
{ type: 'email', message: 'Incorrect email format', trigger: 'blur' },
|
||||
],
|
||||
city: [
|
||||
{ required: true, message: 'Please select the city', trigger: 'change' },
|
||||
],
|
||||
gender: [
|
||||
{ required: true, message: 'Please select gender', trigger: 'change' },
|
||||
],
|
||||
interest: [
|
||||
{ required: true, type: 'array', min: 1, message: 'Choose at least one hobby', trigger: 'change' },
|
||||
{ type: 'array', max: 2, message: 'Choose two hobbies at best', trigger: 'change' },
|
||||
],
|
||||
date: [
|
||||
{ required: true, type: 'date', message: 'Please select the date', trigger: 'change' },
|
||||
],
|
||||
time: [
|
||||
{ required: true, type: 'string', message: 'Please select time', trigger: 'change' },
|
||||
],
|
||||
desc: [
|
||||
{ required: true, message: 'Please enter a personal introduction', trigger: 'blur' },
|
||||
{ type: 'string', min: 20, message: 'Introduce no less than 20 words', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.getList()
|
||||
this.calculateTableHeight()
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
methods: {
|
||||
calculateTableHeight() {
|
||||
// 计算表格高度 = 窗口高度 - 搜索区域高度 - 分页高度 - 其他间距
|
||||
const searchHeight = document.querySelector('.search-area').offsetHeight
|
||||
const actionBtnHeight = document.querySelector('.action-btn').offsetHeight
|
||||
const pageHeight = 32 // 分页组件大约高度
|
||||
const margins = 40 // 上下边距总和
|
||||
this.tableHeight = window.innerHeight - actionBtnHeight - searchHeight - pageHeight - margins - 130
|
||||
},
|
||||
async getList() {
|
||||
await getEventList({ page: 1 }).then((res) => {
|
||||
this.total = res.total
|
||||
this.data = res.data
|
||||
this.page = res.current_page
|
||||
})
|
||||
},
|
||||
handleSubmit(name) {
|
||||
this.$refs[name].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$Message.success('Success!')
|
||||
} else {
|
||||
this.$Message.error('Fail!')
|
||||
}
|
||||
})
|
||||
},
|
||||
handleReset(name) {
|
||||
this.$refs[name].resetFields()
|
||||
},
|
||||
handleSubmit2(name) {
|
||||
this.$refs[name].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$Message.success('Success!')
|
||||
} else {
|
||||
this.$Message.error('Fail!')
|
||||
}
|
||||
})
|
||||
},
|
||||
show(index) {
|
||||
this.$Modal.info({
|
||||
title: 'User Info',
|
||||
content: `Name:${this.data[index].name}<br>Age:${this.data[index].age}<br>Address:${this.data[index].address}`,
|
||||
})
|
||||
},
|
||||
remove(index) {
|
||||
this.data.splice(index, 1)
|
||||
},
|
||||
ok() {
|
||||
this.$Message.info('Clicked ok')
|
||||
},
|
||||
cancel() {
|
||||
this.$Message.info('Clicked cancel')
|
||||
},
|
||||
},
|
||||
name: 'coupon_manage',
|
||||
data() {
|
||||
return {
|
||||
show_modal: false,
|
||||
tableHeight: 500,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
tableLoading: false,
|
||||
submitLoading: false,
|
||||
formInline: {
|
||||
name: '',
|
||||
status: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [
|
||||
{
|
||||
title: '优惠券名称',
|
||||
key: 'name',
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
title: '所属站点',
|
||||
slot: 'station',
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
title: '用户类型',
|
||||
key: 'userTypeText',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
title: '使用门槛(元)',
|
||||
key: 'thresholdText',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
title: '优惠金额(元)',
|
||||
key: 'valueText',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
title: '过期时间',
|
||||
key: 'expirationTimeText',
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'statusText',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createTimeText',
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 180,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
stationOptions: [],
|
||||
userTypeOptions: [
|
||||
{ label: '所有用户', value: 1 },
|
||||
{ label: '会员用户', value: 2 },
|
||||
],
|
||||
statusOptions: [
|
||||
{ label: '开启', value: 1 },
|
||||
{ label: '关闭', value: 0 },
|
||||
],
|
||||
formValidate: createDefaultCouponForm(),
|
||||
ruleValidate: {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入优惠券名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
station_id: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择适用站点',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
user_type: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择用户类型',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
threshold: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入使用门槛',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
value: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入优惠金额',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
expiration_time: [
|
||||
{
|
||||
required: true,
|
||||
type: 'date',
|
||||
message: '请选择过期时间',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
status: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择状态',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.fetchStationOptions()
|
||||
await this.getList()
|
||||
this.$nextTick(this.calculateTableHeight)
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
methods: {
|
||||
// 计算表格高度,保持布局与站点管理页面一<E99DA2>?
|
||||
calculateTableHeight() {
|
||||
const searchArea = document.querySelector('.search-area')
|
||||
const actionArea = document.querySelector('.action-btn')
|
||||
const searchHeight = searchArea ? searchArea.offsetHeight : 0
|
||||
const actionHeight = actionArea ? actionArea.offsetHeight : 0
|
||||
const pageHeight = 32
|
||||
const margins = 40
|
||||
const availableHeight = window.innerHeight - actionHeight - searchHeight - pageHeight - margins - 130
|
||||
this.tableHeight = Math.max(320, availableHeight)
|
||||
},
|
||||
// 拉取站点列表,供下拉选择使用
|
||||
async fetchStationOptions() {
|
||||
try {
|
||||
const res = await getStationList({
|
||||
page: 1,
|
||||
pageSize: 9999,
|
||||
})
|
||||
const list = res && res.data && Array.isArray(res.data.data) ? res.data.data : []
|
||||
this.stationOptions = list
|
||||
.map(item => ({
|
||||
charge_station_id: this.extractStationId(item),
|
||||
charge_station_name: this.extractStationName(item),
|
||||
}))
|
||||
.filter(option => option.charge_station_id)
|
||||
} catch (error) {
|
||||
this.$Message.error('获取站点列表失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
// 搜索优惠券列表
|
||||
handleSubmit() {
|
||||
this.page = 1
|
||||
this.getList()
|
||||
},
|
||||
// 重置搜索条件
|
||||
handleReset() {
|
||||
this.formInline = {
|
||||
name: '',
|
||||
status: '',
|
||||
}
|
||||
this.handleSubmit()
|
||||
},
|
||||
// 查询优惠券列表数据
|
||||
async getList() {
|
||||
const params = {
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
}
|
||||
if (this.formInline.name) {
|
||||
params.name = this.formInline.name
|
||||
}
|
||||
if (this.formInline.status !== '' && this.formInline.status !== null && this.formInline.status !== undefined) {
|
||||
params.status = this.formInline.status
|
||||
}
|
||||
this.tableLoading = true
|
||||
try {
|
||||
const res = await getCouponList(params)
|
||||
const list = res && res.data && Array.isArray(res.data.data) ? res.data.data : []
|
||||
this.total = (res && res.data && res.data.total) || 0
|
||||
this.data = list.map(item => this.normalizeCouponRow(item))
|
||||
} catch (error) {
|
||||
this.$Message.error('获取优惠券列表失败,请稍后重试')
|
||||
} finally {
|
||||
this.tableLoading = false
|
||||
}
|
||||
},
|
||||
// 正常化表格显示字段,保证列表列与站点管理页面风格统一
|
||||
normalizeCouponRow(item) {
|
||||
const stationLabel = this.resolveStationLabel(item)
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name || '--',
|
||||
stationLabel,
|
||||
userTypeText: Number(item.user_type) === 2 ? '会员用户' : '所有用户',
|
||||
thresholdText: this.formatAmount(item.threshold),
|
||||
valueText: this.formatAmount(item.value),
|
||||
expirationTimeText: this.formatTimestamp(item.expiration_time),
|
||||
statusText: Number(item.status) === 1 ? '开启' : '关闭',
|
||||
createTimeText: this.formatTimestamp(item.create_time),
|
||||
}
|
||||
},
|
||||
// 格式化金额,两位小数展示
|
||||
formatAmount(value) {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return '--'
|
||||
}
|
||||
const numberValue = Number(value)
|
||||
if (Number.isNaN(numberValue)) {
|
||||
return '--'
|
||||
}
|
||||
return numberValue.toFixed(2)
|
||||
},
|
||||
// 将时间戳转换为标准日期文案
|
||||
formatTimestamp(timestamp) {
|
||||
if (!timestamp) {
|
||||
return '--'
|
||||
}
|
||||
const value = Number(timestamp)
|
||||
if (Number.isNaN(value)) {
|
||||
return '--'
|
||||
}
|
||||
const date = `${value}`.length === 13 ? new Date(value) : new Date(value * 1000)
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return '--'
|
||||
}
|
||||
const Y = date.getFullYear()
|
||||
const M = `${date.getMonth() + 1}`.padStart(2, '0')
|
||||
const D = `${date.getDate()}`.padStart(2, '0')
|
||||
const h = `${date.getHours()}`.padStart(2, '0')
|
||||
const m = `${date.getMinutes()}`.padStart(2, '0')
|
||||
const s = `${date.getSeconds()}`.padStart(2, '0')
|
||||
return `${Y}-${M}-${D} ${h}:${m}:${s}`
|
||||
},
|
||||
// 抽取站点 ID,兼容不同接口字段
|
||||
extractStationId(item) {
|
||||
const candidateKeys = ['charge_station_id', 'station_id', 'id']
|
||||
for (let i = 0; i < candidateKeys.length; i += 1) {
|
||||
const key = candidateKeys[i]
|
||||
const value = item && item[key]
|
||||
if (value !== undefined && value !== null && `${value}` !== '') {
|
||||
return String(value)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
},
|
||||
// 抽取站点显示名,若接口只返回 ID 则兜底
|
||||
extractStationName(item) {
|
||||
if (!item) {
|
||||
return ''
|
||||
}
|
||||
return item.charge_station_name || item.station_name || item.name || ''
|
||||
},
|
||||
// 根据列表数据推断站点名称
|
||||
resolveStationLabel(item) {
|
||||
if (!item) {
|
||||
return '--'
|
||||
}
|
||||
if (item.station_name) {
|
||||
return item.station_name
|
||||
}
|
||||
if (item.station && item.station.charge_station_name) {
|
||||
return item.station.charge_station_name
|
||||
}
|
||||
const targetId = item.station_id ? String(item.station_id) : ''
|
||||
if (targetId) {
|
||||
const matched = this.stationOptions.find(option => option.charge_station_id === targetId)
|
||||
if (matched) {
|
||||
return matched.charge_station_name
|
||||
}
|
||||
return `站点-${targetId}`
|
||||
}
|
||||
return '--'
|
||||
},
|
||||
// 切换分页
|
||||
onChangePage(page) {
|
||||
if (this.page !== page) {
|
||||
this.page = page
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
onChangePageSize(size) {
|
||||
if (this.pageSize !== size) {
|
||||
this.pageSize = size
|
||||
this.page = 1
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
// 打开新增弹窗
|
||||
add() {
|
||||
this.formValidate = createDefaultCouponForm()
|
||||
this.show_modal = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 查看并编辑任务
|
||||
async show(id) {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getCoupon({ id })
|
||||
const detail = res && res.data ? res.data : {}
|
||||
this.formValidate = {
|
||||
...createDefaultCouponForm(),
|
||||
id: detail.id,
|
||||
name: detail.name || '',
|
||||
station_id: detail.station_id ? String(detail.station_id) : '',
|
||||
user_type: detail.user_type !== undefined ? Number(detail.user_type) : 1,
|
||||
threshold: this.normalizeNumber(detail.threshold),
|
||||
value: this.normalizeNumber(detail.value),
|
||||
expiration_time: this.transformTimestampToDate(detail.expiration_time),
|
||||
status: detail.status !== undefined ? Number(detail.status) : 1,
|
||||
}
|
||||
this.show_modal = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.clearValidate()
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
this.$Message.error('获取优惠券详情失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
// 数字入参归一化
|
||||
normalizeNumber(value) {
|
||||
const numberValue = Number(value)
|
||||
return Number.isNaN(numberValue) ? 0 : numberValue
|
||||
},
|
||||
// 将时间戳转为 DatePicker 可识别的 Date 对象
|
||||
transformTimestampToDate(ts) {
|
||||
if (!ts) {
|
||||
return ''
|
||||
}
|
||||
const value = Number(ts)
|
||||
if (Number.isNaN(value)) {
|
||||
return ''
|
||||
}
|
||||
const date = `${value}`.length === 13 ? new Date(value) : new Date(value * 1000)
|
||||
return Number.isNaN(date.getTime()) ? '' : date
|
||||
},
|
||||
// 点击取消按钮
|
||||
cancel() {
|
||||
this.show_modal = false
|
||||
this.formValidate = createDefaultCouponForm()
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 提交新增/编辑
|
||||
async ok() {
|
||||
const valid = await new Promise(resolve => {
|
||||
this.$refs.formValidate.validate(validRes => {
|
||||
resolve(validRes)
|
||||
})
|
||||
})
|
||||
if (!valid) {
|
||||
this.$Message.error('请完善表单信息后再提交')
|
||||
return
|
||||
}
|
||||
this.submitLoading = true
|
||||
const payload = this.buildSubmitPayload()
|
||||
try {
|
||||
if (payload.id) {
|
||||
const res = await getCouponUpdate(payload)
|
||||
this.$Message.success(res.msg || '更新成功')
|
||||
} else {
|
||||
const res = await addCoupon(payload)
|
||||
this.$Message.success(res.msg || '新增成功')
|
||||
}
|
||||
this.cancel()
|
||||
this.getList()
|
||||
} catch (error) {
|
||||
this.$Message.error(error.msg || '提交失败,请稍后重试')
|
||||
} finally {
|
||||
this.submitLoading = false
|
||||
}
|
||||
},
|
||||
// 组装提交参数,统一在此处理单位转换
|
||||
buildSubmitPayload() {
|
||||
const payload = {
|
||||
...this.formValidate,
|
||||
}
|
||||
payload.station_id = payload.station_id ? Number(payload.station_id) : ''
|
||||
payload.user_type = Number(payload.user_type)
|
||||
payload.threshold = Number(payload.threshold)
|
||||
payload.value = Number(payload.value)
|
||||
payload.status = Number(payload.status)
|
||||
payload.expiration_time = this.transformDateToTimestamp(payload.expiration_time)
|
||||
return payload
|
||||
},
|
||||
// DatePicker 值转时间戳(秒)
|
||||
transformDateToTimestamp(value) {
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return `${value}`.length === 10 ? value : Math.floor(value / 1000)
|
||||
}
|
||||
const date = value instanceof Date ? value : new Date(value)
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return ''
|
||||
}
|
||||
return Math.floor(date.getTime() / 1000)
|
||||
},
|
||||
// 删除优惠券
|
||||
async remove(id) {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
const confirm = await new Promise(resolve => {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '删除后不可恢复,是否继续?',
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => resolve(false),
|
||||
})
|
||||
})
|
||||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getCouponDelete({ id })
|
||||
this.$Message.success(res.msg || '删除成功')
|
||||
if (this.data.length === 1 && this.page > 1) {
|
||||
this.page -= 1
|
||||
}
|
||||
this.getList()
|
||||
} catch (error) {
|
||||
this.$Message.error(error.msg || '删除失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
margin: 20px 0;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
margin: 20px 0;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
.search-area{
|
||||
background-color: white;
|
||||
padding-top: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
|
||||
.search-area {
|
||||
background-color: white;
|
||||
padding-top: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,451 @@
|
||||
<template>
|
||||
<view>safsa</view>
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem label="会员名称:" prop="name">
|
||||
<Input type="text" clearable v-model="formInline.name" placeholder="请输入会员名称" />
|
||||
</FormItem>
|
||||
<FormItem label="状态:" prop="status">
|
||||
<Select v-model="formInline.status" clearable placeholder="全部状态" style="width: 200px">
|
||||
<Option v-for="option in statusOptions" :key="`vip-status-${option.value}`" :value="option.value">
|
||||
{{ option.label }}
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit">搜索</Button>
|
||||
<Button style="margin-left: 8px" @click="handleReset">重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="action-btn">
|
||||
<Button type="primary" @click="add">添加会员</Button>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data" :loading="tableLoading">
|
||||
<template #remarks="{ row }">
|
||||
<Tooltip v-if="row.remarks" transfer :content="row.remarks" placement="top">
|
||||
<span class="ellipsis">{{ row.remarks }}</span>
|
||||
</Tooltip>
|
||||
<span v-else>--</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Button type="primary" size="small" style="margin-right: 5px" @click="show(row.id)">编辑</Button>
|
||||
<Button type="error" size="small" @click="remove(row.id)">删除</Button>
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" :current="page" :page-size="pageSize" show-total show-sizer class="page"
|
||||
@on-change="onChangePage" @on-page-size-change="onChangePageSize" />
|
||||
</div>
|
||||
|
||||
<Modal v-model="show_modal" :title="formValidate.id ? '编辑会员' : '添加会员'" :mask-closable="false"
|
||||
:closable="false" width="600" @on-cancel="cancel">
|
||||
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="120">
|
||||
<FormItem label="会员名称" prop="name">
|
||||
<Input v-model="formValidate.name" placeholder="请输入会员名称" />
|
||||
</FormItem>
|
||||
<FormItem label="会员介绍" prop="remarks">
|
||||
<Input v-model="formValidate.remarks" type="textarea" :autosize="{ minRows: 3, maxRows: 5 }"
|
||||
placeholder="请输入会员权益/说明" />
|
||||
</FormItem>
|
||||
<FormItem label="原价(元)" prop="original_price">
|
||||
<InputNumber v-model="formValidate.original_price" :min="0" :precision="2" :step="1"
|
||||
style="width: 240px" />
|
||||
</FormItem>
|
||||
<FormItem label="售价(元)" prop="selling_price">
|
||||
<InputNumber v-model="formValidate.selling_price" :min="0" :precision="2" :step="1"
|
||||
style="width: 240px" />
|
||||
</FormItem>
|
||||
<FormItem label="有效期(天)" prop="time">
|
||||
<InputNumber v-model="formValidate.time" :min="1" :step="1" style="width: 240px" />
|
||||
</FormItem>
|
||||
<FormItem label="状态" prop="status">
|
||||
<RadioGroup v-model="formValidate.status">
|
||||
<Radio v-for="option in statusOptions" :key="`vip-form-status-${option.value}`" :label="option.value">
|
||||
{{ option.label }}
|
||||
</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div slot="footer">
|
||||
<Button type="text" @click="cancel">取消</Button>
|
||||
<Button type="primary" :loading="submitLoading" @click="ok">确定</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getVipList,
|
||||
addVip,
|
||||
getVip,
|
||||
getVipUpdate,
|
||||
getVipDelete,
|
||||
} from '@/api'
|
||||
|
||||
const createDefaultVipForm = () => ({
|
||||
id: '',
|
||||
name: '',
|
||||
remarks: '',
|
||||
original_price: 0,
|
||||
selling_price: 0,
|
||||
time: 1,
|
||||
status: 1,
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'user_vip',
|
||||
data() {
|
||||
return {
|
||||
show_modal: false,
|
||||
tableHeight: 500,
|
||||
tableLoading: false,
|
||||
submitLoading: false,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
formInline: {
|
||||
name: '',
|
||||
status: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [
|
||||
{ title: '会员名称', key: 'name', minWidth: 160 },
|
||||
{ title: '会员介绍', slot: 'remarks', minWidth: 200 },
|
||||
{ title: '原价(元)', key: 'originalPriceText', minWidth: 120 },
|
||||
{ title: '售价(元)', key: 'sellingPriceText', minWidth: 120 },
|
||||
{ title: '有效期(天)', key: 'timeText', minWidth: 120 },
|
||||
{ title: '状态', key: 'statusText', minWidth: 100 },
|
||||
{ title: '创建时间', key: 'createTimeText', minWidth: 170 },
|
||||
{ title: '操作', slot: 'action', width: 180, align: 'center' },
|
||||
],
|
||||
data: [],
|
||||
statusOptions: [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 },
|
||||
],
|
||||
formValidate: createDefaultVipForm(),
|
||||
ruleValidate: {
|
||||
name: [
|
||||
{ required: true, message: '请输入会员名称', trigger: 'blur' },
|
||||
],
|
||||
remarks: [
|
||||
{ required: false },
|
||||
],
|
||||
original_price: [
|
||||
{ required: true, type: 'number', message: '请输入原价', trigger: 'change' },
|
||||
],
|
||||
selling_price: [
|
||||
{ required: true, type: 'number', message: '请输入售价', trigger: 'change' },
|
||||
],
|
||||
time: [
|
||||
{ required: true, type: 'number', message: '请输入有效期', trigger: 'change' },
|
||||
],
|
||||
status: [
|
||||
{ required: true, type: 'number', message: '请选择状态', trigger: 'change' },
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.getList()
|
||||
this.$nextTick(this.calculateTableHeight)
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
methods: {
|
||||
// 计算表格高度,保持与站点管理页面布局一致
|
||||
calculateTableHeight() {
|
||||
const searchArea = document.querySelector('.search-area')
|
||||
const actionArea = document.querySelector('.action-btn')
|
||||
const searchHeight = searchArea ? searchArea.offsetHeight : 0
|
||||
const actionHeight = actionArea ? actionArea.offsetHeight : 0
|
||||
const pageHeight = 32
|
||||
const margins = 40
|
||||
const availableHeight = window.innerHeight - actionHeight - searchHeight - pageHeight - margins - 130
|
||||
this.tableHeight = Math.max(320, availableHeight)
|
||||
},
|
||||
// 请求会员列表
|
||||
async getList() {
|
||||
const params = {
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
}
|
||||
if (this.formInline.name) {
|
||||
params.name = this.formInline.name
|
||||
}
|
||||
if (this.formInline.status !== '' && this.formInline.status !== null && this.formInline.status !== undefined) {
|
||||
params.status = this.formInline.status
|
||||
}
|
||||
this.tableLoading = true
|
||||
try {
|
||||
const res = await getVipList(params)
|
||||
const list = res && res.data && Array.isArray(res.data.data) ? res.data.data : []
|
||||
this.total = (res && res.data && res.data.total) || 0
|
||||
this.data = list.map(item => this.normalizeVipRow(item))
|
||||
} catch (error) {
|
||||
this.$Message.error('获取会员列表失败,请稍后重试')
|
||||
} finally {
|
||||
this.tableLoading = false
|
||||
}
|
||||
},
|
||||
// 组装表格展示字段
|
||||
normalizeVipRow(item) {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name || '--',
|
||||
remarks: item.remarks || '',
|
||||
originalPriceText: this.formatAmount(item.original_price),
|
||||
sellingPriceText: this.formatAmount(item.selling_price),
|
||||
timeText: this.formatDays(item.time),
|
||||
statusText: Number(item.status) === 1 ? '启用' : '禁用',
|
||||
createTimeText: this.formatTimestamp(item.createtime),
|
||||
updateTimeText: this.formatTimestamp(item.updatetime),
|
||||
}
|
||||
},
|
||||
// 金额格式化
|
||||
formatAmount(value) {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return '--'
|
||||
}
|
||||
const numberValue = Number(value)
|
||||
if (Number.isNaN(numberValue)) {
|
||||
return '--'
|
||||
}
|
||||
return numberValue.toFixed(2)
|
||||
},
|
||||
// 有效期格式化
|
||||
formatDays(value) {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return '--'
|
||||
}
|
||||
const numberValue = Number(value)
|
||||
if (Number.isNaN(numberValue)) {
|
||||
return '--'
|
||||
}
|
||||
return `${numberValue} 天`
|
||||
},
|
||||
// 时间戳转日期
|
||||
formatTimestamp(timestamp) {
|
||||
if (!timestamp) {
|
||||
return '--'
|
||||
}
|
||||
const value = Number(timestamp)
|
||||
if (Number.isNaN(value)) {
|
||||
return '--'
|
||||
}
|
||||
const date = `${value}`.length === 13 ? new Date(value) : new Date(value * 1000)
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return '--'
|
||||
}
|
||||
const Y = date.getFullYear()
|
||||
const M = `${date.getMonth() + 1}`.padStart(2, '0')
|
||||
const D = `${date.getDate()}`.padStart(2, '0')
|
||||
const h = `${date.getHours()}`.padStart(2, '0')
|
||||
const m = `${date.getMinutes()}`.padStart(2, '0')
|
||||
const s = `${date.getSeconds()}`.padStart(2, '0')
|
||||
return `${Y}-${M}-${D} ${h}:${m}:${s}`
|
||||
},
|
||||
// 搜索
|
||||
handleSubmit() {
|
||||
this.page = 1
|
||||
this.getList()
|
||||
},
|
||||
// 重置搜索
|
||||
handleReset() {
|
||||
this.formInline = {
|
||||
name: '',
|
||||
status: '',
|
||||
}
|
||||
this.handleSubmit()
|
||||
},
|
||||
// 分页
|
||||
onChangePage(page) {
|
||||
if (this.page !== page) {
|
||||
this.page = page
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
onChangePageSize(size) {
|
||||
if (this.pageSize !== size) {
|
||||
this.pageSize = size
|
||||
this.page = 1
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
// 打开新增弹窗
|
||||
add() {
|
||||
this.formValidate = createDefaultVipForm()
|
||||
this.show_modal = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 编辑详情
|
||||
async show(id) {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getVip({ id })
|
||||
const detail = res && res.data ? res.data : {}
|
||||
this.formValidate = {
|
||||
...createDefaultVipForm(),
|
||||
id: detail.id,
|
||||
name: detail.name || '',
|
||||
remarks: detail.remarks || '',
|
||||
original_price: this.normalizeNumber(detail.original_price),
|
||||
selling_price: this.normalizeNumber(detail.selling_price),
|
||||
time: this.normalizeNumber(detail.time) || 1,
|
||||
status: detail.status !== undefined ? Number(detail.status) : 1,
|
||||
}
|
||||
this.show_modal = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.clearValidate()
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
this.$Message.error('获取会员详情失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
// 数字归一化
|
||||
normalizeNumber(value) {
|
||||
const numberValue = Number(value)
|
||||
return Number.isNaN(numberValue) ? 0 : numberValue
|
||||
},
|
||||
// 取消操作
|
||||
cancel() {
|
||||
this.show_modal = false
|
||||
this.formValidate = createDefaultVipForm()
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formValidate) {
|
||||
this.$refs.formValidate.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 确认提交
|
||||
async ok() {
|
||||
const valid = await new Promise(resolve => {
|
||||
if (!this.$refs.formValidate) {
|
||||
resolve(false)
|
||||
return
|
||||
}
|
||||
this.$refs.formValidate.validate(result => resolve(result))
|
||||
})
|
||||
if (!valid) {
|
||||
this.$Message.error('请完善表单后再提交')
|
||||
return
|
||||
}
|
||||
this.submitLoading = true
|
||||
const payload = this.buildSubmitPayload()
|
||||
try {
|
||||
if (payload.id) {
|
||||
const res = await getVipUpdate(payload)
|
||||
this.$Message.success(res.msg || '更新成功')
|
||||
} else {
|
||||
const res = await addVip(payload)
|
||||
this.$Message.success(res.msg || '新增成功')
|
||||
}
|
||||
this.cancel()
|
||||
this.getList()
|
||||
} catch (error) {
|
||||
this.$Message.error(error.msg || '提交失败,请稍后重试')
|
||||
} finally {
|
||||
this.submitLoading = false
|
||||
}
|
||||
},
|
||||
// 组装提交参数
|
||||
buildSubmitPayload() {
|
||||
const payload = { ...this.formValidate }
|
||||
payload.original_price = Number(payload.original_price)
|
||||
payload.selling_price = Number(payload.selling_price)
|
||||
payload.time = Number(payload.time)
|
||||
payload.status = Number(payload.status)
|
||||
return payload
|
||||
},
|
||||
// 删除会员配置
|
||||
async remove(id) {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
const confirm = await new Promise(resolve => {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '删除后不可恢复,是否继续?',
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => resolve(false),
|
||||
})
|
||||
})
|
||||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getVipDelete({ id })
|
||||
this.$Message.success(res.msg || '删除成功')
|
||||
if (this.data.length === 1 && this.page > 1) {
|
||||
this.page -= 1
|
||||
}
|
||||
this.getList()
|
||||
} catch (error) {
|
||||
this.$Message.error(error.msg || '删除失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
margin: 20px 0;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.search-area {
|
||||
background-color: white;
|
||||
padding-top: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
display: inline-block;
|
||||
max-width: 160px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@@ -1,17 +1,21 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem prop="user" label="用户手机号">
|
||||
<Input type="text" v-model="formInline.phone" placeholder="" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit('formInline')">搜索</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data">
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem prop="user" label="用户手机号">
|
||||
<Input type="text" clearable v-model="formInline.phone" placeholder="" />
|
||||
</FormItem>
|
||||
<FormItem prop="order_number" label="订单编号">
|
||||
<Input type="text" clearable v-model="formInline.order_number" placeholder="" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit('formInline')"><Icon type="ios-search" />搜索</Button>
|
||||
<Button style="margin-left: 8px" @click="handleReset('formInline')"><Icon type="md-refresh" />重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data">
|
||||
<template #order="{ row, index }">
|
||||
订单编号: {{ row.order_number }}<br>
|
||||
开始时间: {{ row.start_time }}<br>
|
||||
@@ -40,162 +44,173 @@
|
||||
{{row.is_wind == 0 ? '未结算' : '已结算'}}
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ row, index }">
|
||||
<!-- <Button type="primary" size="small" style="margin-right: 5px" @click="show(index)">详情</Button> -->
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" show-total show-sizer class="page" @on-change="onChangePage"
|
||||
@on-page-size-change="onChangePageSize"/>
|
||||
</div>
|
||||
<template #action="{ row, index }">
|
||||
<!-- <Button type="primary" size="small" style="margin-right: 5px" @click="show(index)">详情</Button> -->
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" show-total show-sizer class="page" @on-change="onChangePage"
|
||||
@on-page-size-change="onChangePageSize" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getChargeOrder } from '@/api'
|
||||
import {
|
||||
getChargeOrder,
|
||||
} from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'charging_station',
|
||||
data() {
|
||||
return {
|
||||
show_modal: false,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
formInline: {
|
||||
user: '',
|
||||
password: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'order_id',
|
||||
width: '80',
|
||||
},
|
||||
{
|
||||
title: '订单信息',
|
||||
slot: 'order',
|
||||
width: '280',
|
||||
},
|
||||
{
|
||||
title: '充电站信息',
|
||||
slot: 'pile',
|
||||
width: '240',
|
||||
},
|
||||
{
|
||||
title: '用户信息',
|
||||
slot: 'user',
|
||||
width: '310',
|
||||
},
|
||||
{
|
||||
title: '充电信息',
|
||||
slot: 'power',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '充电类型',
|
||||
slot: 'type',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
this.calculateTableHeight()
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
methods: {
|
||||
calculateTableHeight() {
|
||||
// 计算表格高度 = 窗口高度 - 搜索区域高度 - 分页高度 - 其他间距
|
||||
const searchHeight = document.querySelector('.search-area').offsetHeight
|
||||
const pageHeight = 32 // 分页组件大约高度
|
||||
const margins = 40 // 上下边距总和
|
||||
this.tableHeight = window.innerHeight - searchHeight - pageHeight - margins - 130
|
||||
},
|
||||
async getList() {
|
||||
await getChargeOrder({ page: this.page, pageSize: this.pageSize, ...this.formInline }).then((res) => {
|
||||
this.total = res.total
|
||||
this.data = res.data
|
||||
this.page = res.current_page
|
||||
})
|
||||
},
|
||||
handleSubmit() {
|
||||
this.getList()
|
||||
},
|
||||
handleReset(name) {
|
||||
this.$refs[name].resetFields()
|
||||
},
|
||||
show(index) {
|
||||
this.$Modal.info({
|
||||
title: 'User Info',
|
||||
content: `Name:${this.data[index].name}<br>Age:${this.data[index].age}<br>Address:${this.data[index].address}`,
|
||||
})
|
||||
},
|
||||
onChangePage(e) {
|
||||
if (this.page != e) {
|
||||
this.page = e
|
||||
this.getList()
|
||||
export default {
|
||||
name: 'charging_station',
|
||||
data() {
|
||||
return {
|
||||
show_modal: false,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
formInline: {
|
||||
phone: '',
|
||||
order_number: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [{
|
||||
title: 'id',
|
||||
key: 'order_id',
|
||||
width: '80',
|
||||
},
|
||||
{
|
||||
title: '订单信息',
|
||||
slot: 'order',
|
||||
width: '280',
|
||||
},
|
||||
{
|
||||
title: '充电站信息',
|
||||
slot: 'pile',
|
||||
width: '240',
|
||||
},
|
||||
{
|
||||
title: '用户信息',
|
||||
slot: 'user',
|
||||
width: '310',
|
||||
},
|
||||
{
|
||||
title: '充电信息',
|
||||
slot: 'power',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '充电类型',
|
||||
slot: 'type',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
}
|
||||
},
|
||||
onChangePageSize(e) {
|
||||
if (this.pageSize != e) {
|
||||
this.page = 1
|
||||
this.pageSize = e
|
||||
this.getList()
|
||||
}
|
||||
mounted() {
|
||||
this.getList()
|
||||
this.calculateTableHeight()
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
},
|
||||
}
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
methods: {
|
||||
calculateTableHeight() {
|
||||
// 计算表格高度 = 窗口高度 - 搜索区域高度 - 分页高度 - 其他间距
|
||||
const searchHeight = document.querySelector('.search-area').offsetHeight
|
||||
const pageHeight = 32 // 分页组件大约高度
|
||||
const margins = 40 // 上下边距总和
|
||||
this.tableHeight = window.innerHeight - searchHeight - pageHeight - margins - 130
|
||||
},
|
||||
async getList() {
|
||||
await getChargeOrder({
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
...this.formInline,
|
||||
}).then((res) => {
|
||||
this.total = res.total
|
||||
this.data = res.data
|
||||
this.page = res.current_page
|
||||
})
|
||||
},
|
||||
handleSubmit() {
|
||||
this.getList()
|
||||
},
|
||||
handleReset() {
|
||||
this.formInline = {
|
||||
phone: '',
|
||||
order_number: '',
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
show(index) {
|
||||
this.$Modal.info({
|
||||
title: 'User Info',
|
||||
content: `Name:${this.data[index].name}<br>Age:${this.data[index].age}<br>Address:${this.data[index].address}`,
|
||||
})
|
||||
},
|
||||
onChangePage(e) {
|
||||
if (this.page != e) {
|
||||
this.page = e
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
onChangePageSize(e) {
|
||||
if (this.pageSize != e) {
|
||||
this.page = 1
|
||||
this.pageSize = e
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
margin: 20px 0;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
.action-btn {
|
||||
margin: 20px 0;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.page {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
.search-area{
|
||||
background-color: white;
|
||||
padding-top: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.table-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.search-area {
|
||||
background-color: white;
|
||||
padding-top: 20px;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -2,17 +2,26 @@
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<FormItem prop="user" label="用户">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
<FormItem prop="openid" label="openid">
|
||||
<Input type="text" v-model="formInline.openid" placeholder="" />
|
||||
</FormItem>
|
||||
<FormItem prop="user" label="类型">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
<FormItem prop="phone" label="手机号">
|
||||
<Input type="text" v-model="formInline.phone" placeholder="" />
|
||||
</FormItem>
|
||||
<FormItem prop="user" label="起止时间">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
<FormItem prop="type" label="类型">
|
||||
<Select v-model="formInline.type" style="width:200px">
|
||||
<Option value="1">充电订单</Option>
|
||||
<Option value="2">充值</Option>
|
||||
<Option value="3">提现</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem prop="" label="起止时间">
|
||||
<DatePicker type="daterange" placement="bottom-end" placeholder="请选择时间" style="width: 200px"
|
||||
@on-change="chage" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit('formInline')">搜索</Button>
|
||||
<Button type="primary" @click="handleSubmit('formInline')"><Icon type="ios-search" />搜索</Button>
|
||||
<Button style="margin-left: 8px" @click="resetSearch"><Icon type="md-refresh" />重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
@@ -30,7 +39,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCaiWuData } from '@/api'
|
||||
import {
|
||||
getCaiWuData,
|
||||
} from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'charging_station',
|
||||
@@ -42,8 +53,11 @@
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
formInline: {
|
||||
user: '',
|
||||
password: '',
|
||||
openid: '',
|
||||
type: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
phone: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [{
|
||||
@@ -54,6 +68,10 @@
|
||||
title: '用户',
|
||||
key: 'openid',
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
key: 'phone',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
key: 'type_text',
|
||||
@@ -100,23 +118,25 @@
|
||||
await getCaiWuData({
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
...this.formInline,
|
||||
}).then((res) => {
|
||||
this.total = res.total
|
||||
this.data = res.data
|
||||
this.page = res.current_page
|
||||
})
|
||||
},
|
||||
handleSubmit(name) {
|
||||
this.$refs[name].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$Message.success('Success!')
|
||||
} else {
|
||||
this.$Message.error('Fail!')
|
||||
}
|
||||
})
|
||||
handleSubmit() {
|
||||
this.getList()
|
||||
},
|
||||
handleReset(name) {
|
||||
this.$refs[name].resetFields()
|
||||
resetSearch(name) {
|
||||
this.formInline = {
|
||||
openid: '',
|
||||
type: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
phone: '',
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
show(index) {
|
||||
this.$Modal.info({
|
||||
@@ -137,6 +157,12 @@
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
chage(e) {
|
||||
this.formInline.start_time = ''
|
||||
this.formInline.end_time = ''
|
||||
this.formInline.start_time = e[0]
|
||||
this.formInline.end_time = e[1]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,309 @@
|
||||
<template>
|
||||
<view>11</view>
|
||||
<div>
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" :model="formInline" inline :label-width="120">
|
||||
<FormItem prop="user" label="时间">
|
||||
<Select v-model="formInline.time_type" clearable style="width:200px">
|
||||
<Option :value="1">天</Option>
|
||||
<Option :value="4">周</Option>
|
||||
<Option :value="2">月</Option>
|
||||
<Option :value="3">年</Option>
|
||||
<Option :value="5">自定义</Option>
|
||||
</Select>
|
||||
<DatePicker v-if="formInline.time_type == 1" :value="formInline.time" type="date"
|
||||
placeholder="请选择日期" style="width: 200px" @on-change="chage" />
|
||||
<DatePicker v-if="formInline.time_type == 2" type="month" placeholder="请选择日期" style="width: 200px"
|
||||
@on-change="chage" />
|
||||
<DatePicker v-if="formInline.time_type == 3" type="year" placeholder="请选择日期" style="width: 200px"
|
||||
@on-change="chage" />
|
||||
<DatePicker v-if="formInline.time_type == 4" type="daterange" show-week-numbers
|
||||
placement="bottom-end" placeholder="请选择日期" style="width: 200px" @on-change="chage" />
|
||||
<DatePicker v-if="formInline.time_type == 5" type="daterange" placement="bottom-end"
|
||||
placeholder="请选择日期" style="width: 200px" @on-change="chage" />
|
||||
</FormItem>
|
||||
<FormItem prop="password" label="充电站">
|
||||
<Select v-model="formInline.charge_station_id" style="width:200px" clearable
|
||||
@on-change="changeStation">
|
||||
<Option v-for="item in stationList" :value="item.charge_station_id"
|
||||
:key="item.charge_station_id">{{ item.charge_station_name }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem prop="password" label="充电桩">
|
||||
<Select v-model="formInline.connectorID" style="width:200px" clearable>
|
||||
<Option v-for="item in pileList" :value="item.ConnectorID" :key="item.charge_pile_id">
|
||||
{{ item.ConnectorID }}
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit('formInline')">搜索</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div id="main" ref="chartContainer" style="width: 100%; height: 400px;"></div>
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data">
|
||||
<template #action="{ row, index }">
|
||||
<!-- <Button type="primary" size="small" style="margin-right: 5px" @click="show(index)">详情</Button> -->
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" show-total show-sizer class="page" @on-change="onChangePage"
|
||||
@on-page-size-change="onChangePageSize" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
import {
|
||||
getChargePile,
|
||||
getStationList,
|
||||
getPileList,
|
||||
} from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'Total',
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
chartInstance: null,
|
||||
formInline: {
|
||||
charge_station_id: '',
|
||||
time_type: 1,
|
||||
connectorID: '',
|
||||
time: '',
|
||||
},
|
||||
stationList: [],
|
||||
pileList: [],
|
||||
columns: [{
|
||||
title: '充电桩编号',
|
||||
key: 'ConnectorID',
|
||||
},
|
||||
{
|
||||
title: '充电站名称',
|
||||
key: 'charge_station_name',
|
||||
},
|
||||
{
|
||||
title: '总电量',
|
||||
key: 'totalPower',
|
||||
},
|
||||
{
|
||||
title: '总时间',
|
||||
key: 'totalTimeFormatted',
|
||||
},
|
||||
{
|
||||
title: '总费用',
|
||||
key: 'totalPrice',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
this.getStationList()
|
||||
this.getPileList()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.chartInstance) {
|
||||
this.chartInstance.dispose()
|
||||
}
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
},
|
||||
methods: {
|
||||
async initChart() {
|
||||
if (!this.$refs.chartContainer) return
|
||||
|
||||
// 销毁旧实例
|
||||
if (this.chartInstance) {
|
||||
this.chartInstance.dispose()
|
||||
}
|
||||
|
||||
this.formInline.time = new Date().toLocaleDateString().replace('///g', '-')
|
||||
this.chartInstance = echarts.init(this.$refs.chartContainer)
|
||||
|
||||
const xAxisData = []
|
||||
const powerData = []
|
||||
const priceData = []
|
||||
const timeData = []
|
||||
|
||||
await getChargePile({
|
||||
...this.formInline,
|
||||
}).then((res) => {
|
||||
this.data = res
|
||||
this.$nextTick()
|
||||
res.forEach((i) => {
|
||||
xAxisData.push(i.no)
|
||||
powerData.push(parseFloat(i.totalPower))
|
||||
priceData.push(parseFloat(i.totalPrice))
|
||||
timeData.push(parseFloat(i.totalTimeFormatted))
|
||||
})
|
||||
})
|
||||
const option = {
|
||||
color: ['#409eff', '#67C23A', '#E6A23C'],
|
||||
grid: {
|
||||
left: 5,
|
||||
right: 15,
|
||||
top: 40,
|
||||
bottom: 5,
|
||||
containLabel: true,
|
||||
},
|
||||
title: {
|
||||
text: '充电量、费用及时长统计',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
const [power, price, time] = params
|
||||
return `
|
||||
${power.axisValue}<br>
|
||||
${power.seriesName}:${power.data} KW<br>
|
||||
${price.seriesName}:${price.data} 元<br>
|
||||
${time.seriesName}:${time.data} 小时
|
||||
`
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ['总电量', '总费用', '总时长'],
|
||||
right: 80, // 距离右侧 10px
|
||||
top: 10, // 垂直居中
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xAxisData,
|
||||
},
|
||||
yAxis: [{
|
||||
type: 'value',
|
||||
name: '电量/KW',
|
||||
axisLabel: {
|
||||
formatter: '{value} KW',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '费用/元',
|
||||
axisLabel: {
|
||||
formatter: '{value} 元',
|
||||
},
|
||||
min: 0,
|
||||
max: (value) => Math.ceil(value.max / 100) * 100, // 自动调整为100的整数倍
|
||||
interval: 100, // 每100元一个刻度
|
||||
splitNumber: 5, // 建议5-7个刻度
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#EE6666', // 设置轴线颜色与对应系列一致
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [{
|
||||
name: '总电量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
data: powerData,
|
||||
},
|
||||
{
|
||||
name: '总费用',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
data: priceData,
|
||||
},
|
||||
{
|
||||
name: '总时长',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
yAxisIndex: 1,
|
||||
data: timeData,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
this.chartInstance.setOption(option)
|
||||
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
},
|
||||
|
||||
handleResize() {
|
||||
if (this.chartInstance) {
|
||||
this.chartInstance.resize()
|
||||
}
|
||||
},
|
||||
async getStationList() {
|
||||
await getStationList({
|
||||
page: 1,
|
||||
pageSize: 999,
|
||||
}).then((res) => {
|
||||
this.stationList = res.data.data
|
||||
})
|
||||
},
|
||||
async getPileList() {
|
||||
await getPileList({
|
||||
page: 1,
|
||||
pageSize: 999,
|
||||
charge_station_id: this.formInline.charge_station_id,
|
||||
}).then((res) => {
|
||||
this.pileList = res.data.data
|
||||
})
|
||||
},
|
||||
chage(e) {
|
||||
this.formInline.time = ''
|
||||
this.formInline.start_time = ''
|
||||
this.formInline.end_time = ''
|
||||
if (this.formInline.time_type == 4 || this.formInline.time_type == 5) {
|
||||
this.formInline.start_time = e[0]
|
||||
this.formInline.end_time = e[1]
|
||||
} else {
|
||||
this.formInline.time = e
|
||||
}
|
||||
},
|
||||
handleSubmit() {
|
||||
this.initChart()
|
||||
},
|
||||
changeStation() {
|
||||
this.getPileList()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
#main {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.search-area {
|
||||
background-color: white;
|
||||
border: 1px solid #F3F7FD;
|
||||
border-radius: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -2,17 +2,40 @@
|
||||
<div class="container">
|
||||
<div class="search-area">
|
||||
<Form ref="formInline" inline :label-width="120" :model="formInline" :rules="ruleInline">
|
||||
<div>
|
||||
<FormItem prop="user" label="报表类型">
|
||||
<Space direction="vertical" size="large">
|
||||
<RadioGroup v-model="formInline.animal" @on-change="changeRadio">
|
||||
<Radio :label="1">充电桩分类运营统计</Radio>
|
||||
<Radio :label="2">充电桩单桩统计</Radio>
|
||||
<Radio :label="3" disabled>VIN充电统计</Radio>
|
||||
<Radio :label="4" disabled>二维码充电统计</Radio>
|
||||
<Radio :label="5">充电订单流水</Radio>
|
||||
</RadioGroup>
|
||||
</Space>
|
||||
</FormItem>
|
||||
</div>
|
||||
<FormItem prop="user" label="站点">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
<Select v-model="formInline.charge_station_id" style="width:200px" @on-change="changeStation">
|
||||
<Option v-for="item in stationData" :value="item.charge_station_id"
|
||||
:key="item.charge_station_id">{{ item.charge_station_name }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem prop="user" label="充电桩">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
<FormItem prop="user" label="充电桩" v-if="formInline.animal == 2">
|
||||
<Select v-model="formInline.charge_pile_id" style="width:200px">
|
||||
<Option v-for="item in pileData" :value="item.charge_pile_id" :key="item.charge_pile_id">
|
||||
{{ item.ConnectorID }}
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem prop="user" label="起止时间">
|
||||
<Input type="text" v-model="formInline.user" placeholder="" />
|
||||
<DatePicker type="daterange" placement="bottom-end" placeholder="请选择时间" style="width: 200px"
|
||||
@on-change="chage" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="handleSubmit('formInline')">搜索</Button>
|
||||
<Button type="primary" @click="handleSubmit('formInline')"><Icon type="ios-search" />生成报表</Button>
|
||||
<Button style="margin-left: 8px" @click="resetSearch"><Icon type="md-refresh" />重置</Button>
|
||||
<!-- <Button style="margin-left: 8px" @click="report"><Icon type="md-arrow-down" />导出报表</Button> -->
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
@@ -30,7 +53,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getYunYingData } from '@/api'
|
||||
import {
|
||||
getYunYingData,
|
||||
getStationList,
|
||||
getPileList,
|
||||
} from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'charging_station',
|
||||
@@ -42,11 +69,15 @@
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
formInline: {
|
||||
user: '',
|
||||
password: '',
|
||||
animal: 1,
|
||||
charge_station_id: '',
|
||||
charge_pile_id: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
},
|
||||
ruleInline: {},
|
||||
columns: [{
|
||||
columns: [],
|
||||
columns_1: [{
|
||||
title: '充电站名称',
|
||||
key: 'charge_station_name',
|
||||
},
|
||||
@@ -93,11 +124,90 @@
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
columns_2: [{
|
||||
title: '充电桩ID',
|
||||
key: 'charge_pile_id',
|
||||
},
|
||||
{
|
||||
title: '桩名称',
|
||||
key: 'ConnectorID',
|
||||
},
|
||||
{
|
||||
title: '充电站名称',
|
||||
key: 'charge_station_name',
|
||||
},
|
||||
{
|
||||
title: '枪数量',
|
||||
key: 'pile_num',
|
||||
},
|
||||
{
|
||||
title: '充电次数(次)',
|
||||
key: 'order_num',
|
||||
},
|
||||
{
|
||||
title: '充电电量(度)',
|
||||
key: 'power',
|
||||
},
|
||||
{
|
||||
title: '充电金额(元)',
|
||||
key: 'money',
|
||||
},
|
||||
{
|
||||
title: '电费(元)',
|
||||
key: 'elec',
|
||||
},
|
||||
{
|
||||
title: '服务费(元)',
|
||||
key: 'sevice',
|
||||
},
|
||||
{
|
||||
title: '总时长(小时)',
|
||||
key: 'length',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
columns_5: [{
|
||||
title: '充电订单号',
|
||||
key: 'order_number',
|
||||
},
|
||||
{
|
||||
title: '充电站名称',
|
||||
key: 'charge_station_name',
|
||||
},
|
||||
{
|
||||
title: '桩名称',
|
||||
key: 'ConnectorID',
|
||||
},
|
||||
{
|
||||
title: '充电停止原因',
|
||||
key: 'stop_type_text',
|
||||
},
|
||||
{
|
||||
title: '车架号',
|
||||
key: 'vin',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
phone: '',
|
||||
stationData: [],
|
||||
pileData: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
this.getStationList()
|
||||
this.getPileList()
|
||||
this.calculateTableHeight()
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
@@ -113,25 +223,51 @@
|
||||
this.tableHeight = window.innerHeight - searchHeight - pageHeight - margins - 130
|
||||
},
|
||||
async getList() {
|
||||
if (this.formInline.animal == 1) {
|
||||
this.columns = this.columns_1
|
||||
} else if (this.formInline.animal == 2) {
|
||||
this.columns = this.columns_2
|
||||
} else if (this.formInline.animal == 5) {
|
||||
this.columns = this.columns_5
|
||||
}
|
||||
await getYunYingData({
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
...this.formInline,
|
||||
}).then((res) => {
|
||||
this.total = res.data.total
|
||||
this.data = res.data.data
|
||||
})
|
||||
},
|
||||
handleSubmit(name) {
|
||||
this.$refs[name].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$Message.success('Success!')
|
||||
} else {
|
||||
this.$Message.error('Fail!')
|
||||
}
|
||||
async getStationList() {
|
||||
await getStationList({
|
||||
page: 1,
|
||||
pageSize: 999,
|
||||
}).then((res) => {
|
||||
this.stationData = res.data.data
|
||||
})
|
||||
},
|
||||
handleReset(name) {
|
||||
this.$refs[name].resetFields()
|
||||
async getPileList() {
|
||||
await getPileList({
|
||||
page: 1,
|
||||
pageSize: 999,
|
||||
charge_station_id: this.formInline.charge_station_id,
|
||||
}).then((res) => {
|
||||
this.pileData = res.data.data
|
||||
})
|
||||
},
|
||||
handleSubmit() {
|
||||
this.getList()
|
||||
},
|
||||
resetSearch() {
|
||||
this.formInline = {
|
||||
animal: 1,
|
||||
charge_station_id: '',
|
||||
charge_pile_id: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
show(index) {
|
||||
this.$Modal.info({
|
||||
@@ -152,6 +288,24 @@
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
changeStation() {
|
||||
if (this.formInline.animal == 2) {
|
||||
this.getPileList()
|
||||
}
|
||||
},
|
||||
chage(e) {
|
||||
this.formInline.start_time = ''
|
||||
this.formInline.end_time = ''
|
||||
this.formInline.start_time = e[0]
|
||||
this.formInline.end_time = e[1]
|
||||
},
|
||||
changeRadio(e) {
|
||||
this.page = 1
|
||||
this.pageSize = 10
|
||||
},
|
||||
report() {
|
||||
this.$Message.info('测试中')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -196,17 +196,10 @@
|
||||
const pageHeight = 32 // 分页组件大约高度
|
||||
const margins = 40 // 上下边距总和
|
||||
this.tableHeight = window.innerHeight - searchHeight - pageHeight - margins - 230
|
||||
console.log(this.tableHeight)
|
||||
},
|
||||
async fetchRoles() {
|
||||
// 角色列表仅需偶尔变动,进入页面时加载一次即可
|
||||
try {
|
||||
const res = await getRoleList()
|
||||
const list = res.data || res
|
||||
this.roleOptions = Array.isArray(list) ? list : []
|
||||
} catch (error) {
|
||||
this.roleOptions = []
|
||||
}
|
||||
const res = await getRoleList()
|
||||
this.roleOptions = res.data.data
|
||||
},
|
||||
async getList() {
|
||||
this.tableLoading = true
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
<div class="table-container">
|
||||
<Table border :columns="columns" stripe :height="tableHeight" :data="data">
|
||||
<template #action="{ row, index }">
|
||||
<Button type="primary" size="small" style="margin-right: 5px" @click="show(index)">编辑</Button>
|
||||
<Button type="error" size="small" @click="remove(index)">删除</Button>
|
||||
<Button type="primary" size="small" style="margin-right: 5px" @click="show(row.id)"
|
||||
v-if="row.id != 1">编辑</Button>
|
||||
<Button type="error" size="small" @click="remove(row.id)" v-if="row.id != 1">删除</Button>
|
||||
</template>
|
||||
</Table>
|
||||
<Page :total="total" show-total show-sizer class="page" />
|
||||
<Page :total="total" show-total show-sizer class="page" @on-change="onChangePage"
|
||||
@on-page-size-change="onChangePageSize" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,12 +35,12 @@
|
||||
</FormItem>
|
||||
<FormItem label="所属上级" prop="pid">
|
||||
<Select v-model="formValidate.pid" style="width:200px">
|
||||
<Option :value="0">无</Option>
|
||||
<Option v-for="item in roles_data" :value="item.id" :key="item.id">{{ item.name }}</Option>
|
||||
<Option v-for="item in data" :value="item.id" :key="item.id">{{ item.name }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="权限菜单" prop="roles">
|
||||
<Tree :data="menu_data" ref="tree" show-checkbox style="height: 300px;overflow-y: auto;"></Tree>
|
||||
<Tree :data="menu_data" :checked-keys="defaultCheckedKeys" ref="tree" show-checkbox
|
||||
style="height: 300px;overflow-y: auto;"></Tree>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div slot="footer">
|
||||
@@ -53,6 +55,10 @@
|
||||
import {
|
||||
GetRole,
|
||||
getMenuList,
|
||||
addRole,
|
||||
getRoleRead,
|
||||
getRoleUpdate,
|
||||
getRoleDelete,
|
||||
} from '@/api'
|
||||
|
||||
export default {
|
||||
@@ -60,6 +66,8 @@
|
||||
data() {
|
||||
return {
|
||||
show_modal: false,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 100,
|
||||
tableHeight: 500,
|
||||
formInline: {
|
||||
@@ -84,6 +92,7 @@
|
||||
],
|
||||
data: [],
|
||||
formValidate: {
|
||||
id: '',
|
||||
name: '',
|
||||
pid: 0,
|
||||
roles: '',
|
||||
@@ -103,38 +112,13 @@
|
||||
value: 'New York',
|
||||
label: 'New York',
|
||||
}],
|
||||
menu_data: [{
|
||||
title: 'parent 1',
|
||||
expand: false,
|
||||
children: [{
|
||||
title: 'parent 1-1',
|
||||
expand: true,
|
||||
children: [{
|
||||
title: 'leaf 1-1-1',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
title: 'leaf 1-1-2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'parent 1-2',
|
||||
expand: true,
|
||||
children: [{
|
||||
title: 'leaf 1-2-1',
|
||||
},
|
||||
{
|
||||
title: 'leaf 1-2-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}],
|
||||
menu_data: [],
|
||||
defaultCheckedKeys: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
this.getMenuList()
|
||||
this.calculateTableHeight()
|
||||
window.addEventListener('resize', this.calculateTableHeight)
|
||||
},
|
||||
@@ -152,11 +136,11 @@
|
||||
},
|
||||
async getList() {
|
||||
await GetRole({
|
||||
page: 1,
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
}).then((res) => {
|
||||
this.total = res.total
|
||||
this.data = res.data
|
||||
this.page = res.current_page
|
||||
this.total = res.data.total
|
||||
this.data = res.data.data
|
||||
})
|
||||
},
|
||||
handleSubmit(name) {
|
||||
@@ -168,14 +152,36 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
show(index) {
|
||||
this.$Modal.info({
|
||||
title: 'User Info',
|
||||
content: `Name:${this.data[index].name}<br>Age:${this.data[index].age}<br>Address:${this.data[index].address}`,
|
||||
async show(id) {
|
||||
await getRoleRead({
|
||||
id,
|
||||
}).then((res) => {
|
||||
this.show_modal = true
|
||||
this.formValidate = {
|
||||
...res.data,
|
||||
}
|
||||
this.getMenuList()
|
||||
})
|
||||
},
|
||||
remove(index) {
|
||||
this.data.splice(index, 1)
|
||||
async remove(id) {
|
||||
const confirm = await new Promise(resolve => {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '您确定要删除此项吗?',
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => resolve(false),
|
||||
})
|
||||
})
|
||||
if (!confirm) {
|
||||
this.$Message.info('已取消删除')
|
||||
return
|
||||
}
|
||||
await getRoleDelete({
|
||||
id,
|
||||
}).then((res) => {
|
||||
this.$Message.success(res.msg)
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
async ok() {
|
||||
let menuIds = this.$refs.tree.getCheckedAndIndeterminateNodes()
|
||||
@@ -185,22 +191,58 @@
|
||||
this.$Message.error('请正确填写表单')
|
||||
return
|
||||
}
|
||||
// if (this.formValidate.id) { } else { }
|
||||
this.$Message.info('Clicked ok')
|
||||
if (this.formValidate.id) {
|
||||
await getRoleUpdate(this.formValidate).then((res) => {
|
||||
this.$Message.success(res.msg)
|
||||
this.getList()
|
||||
this.cancel()
|
||||
})
|
||||
} else {
|
||||
await addRole(this.formValidate).then((res) => {
|
||||
this.$Message.success(res.msg)
|
||||
this.getList()
|
||||
this.cancel()
|
||||
})
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
this.show_modal = false
|
||||
this.$refs.formValidate.resetFields()
|
||||
this.formValidate = {
|
||||
id: '',
|
||||
name: '',
|
||||
pid: 0,
|
||||
roles: '',
|
||||
}
|
||||
},
|
||||
async add() {
|
||||
this.show_modal = true
|
||||
await this.getMenuList()
|
||||
this.formValidate = {
|
||||
id: '',
|
||||
name: '',
|
||||
pid: 0,
|
||||
roles: '',
|
||||
}
|
||||
},
|
||||
async getMenuList() {
|
||||
await getMenuList().then((res) => {
|
||||
await getMenuList({
|
||||
roles: this.formValidate.roles,
|
||||
}).then((res) => {
|
||||
this.menu_data = res
|
||||
})
|
||||
},
|
||||
onChangePage(e) {
|
||||
if (this.page != e) {
|
||||
this.page = e
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
onChangePageSize(e) {
|
||||
if (this.pageSize != e) {
|
||||
this.page = 1
|
||||
this.pageSize = e
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user