This commit is contained in:
yaosen 2024-06-28 17:30:56 +08:00
parent f3ca8f3812
commit 054258a450
13 changed files with 1448 additions and 246 deletions

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@ -162,12 +162,21 @@ export const asyncRoutes = [
}
},
{
path: 'product',
component: () => import('@/views/order/product'),
name: 'productNameList',
path: 'abandoned',
component: () => import('@/views/order/abandoned'),
name: 'OrderBack',
meta: {
title: '产品统计',
roles: ['order_pub', 'editor']
title: '已放弃订单',
roles: ['order_back', 'editor']
}
},
{
path: 'used',
component: () => import('@/views/order/used'),
name: 'OrderBack',
meta: {
title: '已使用订单',
roles: ['order_back', 'editor']
}
},
]
@ -198,7 +207,7 @@ export const asyncRoutes = [
component: () => import('@/views/qa/qa.vue'),
name: 'qa',
meta: {
title: 'QA列表',
title: 'QA管理列表',
roles: ['admin']
}
},
@ -207,45 +216,12 @@ export const asyncRoutes = [
component: () => import('@/views/qa/city.vue'),
name: 'city',
meta: {
title: '城市列表',
title: '城市管理列表',
roles: ['admin']
}
}
]
},
{
path: '/log',
component: Layout,
redirect: '/log/index',
alwaysShow: true,
name: 'Log',
meta: {
title: '日志记录',
icon: 'nested',
roles: ['follow_index', 'log_index', 'editor']
},
children: [
{
path: 'follow',
component: () => import('@/views/log/follow'),
name: 'Follow',
meta: {
title: '跟进记录',
roles: ['follow_index', 'editor']
}
},
{
path: 'index',
component: () => import('@/views/log/index'),
name: 'LogIndex',
meta: {
title: '日志记录',
roles: ['log_index']
}
}
]
},
{
path: '/data',
component: Layout,
@ -258,6 +234,15 @@ export const asyncRoutes = [
roles: ['data_index']
},
children: [
{
path: 'product',
component: () => import('@/views/order/product'),
name: 'productNameList',
meta: {
title: '产品统计',
roles: ['order_pub', 'editor']
}
},
{
path: 'index',
component: () => import('@/views/data/index'),
@ -295,8 +280,38 @@ export const asyncRoutes = [
}
}
]
},{
path: '/log',
component: Layout,
redirect: '/log/index',
alwaysShow: true,
name: 'Log',
meta: {
title: '日志记录',
icon: 'nested',
roles: ['follow_index', 'log_index', 'editor']
},
children: [
{
path: 'follow',
component: () => import('@/views/log/follow'),
name: 'Follow',
meta: {
title: '跟进记录',
roles: ['follow_index', 'editor']
}
},
{
path: 'index',
component: () => import('@/views/log/index'),
name: 'LogIndex',
meta: {
title: '日志记录',
roles: ['log_index']
}
}
]
},
{
path: '/icon',
component: Layout,

View File

@ -104,7 +104,7 @@
</div>
</div>
</el-col>
<!-- <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleOrder({os_status:[4,3],times:[handleGetStartTime(30),handleEndTime()]})">
<div class="card-panel-icon-wrapper icon-camellia">
<svg-icon icon-class="documentation" class-name="card-panel-icon" />
@ -181,7 +181,7 @@
<count-to :start-val="0" :end-val="refund_price_80" :duration="10" class="card-panel-num" />
</div>
</div>
</el-col>-->
</el-col>
</el-row>
</template>
@ -221,6 +221,12 @@ export default {
this.asset_price = parseFloat(res.data.asset_price)/100
this.refund = parseFloat(res.data.refund)
this.refund_price = parseFloat(res.data.refund_price)/100
this.refund_30 = parseFloat(res.data.refund_30)
this.refund_price_30 = parseFloat(res.data.refund_price_30)/100
this.refund_60 = parseFloat(res.data.refund_60)
this.refund_price_60 = parseFloat(res.data.refund_price_60)/100
this.refund_80 = parseFloat(res.data.refund_80)
this.refund_price_80 = parseFloat(res.data.refund_price_80)/100
}).catch(err=>{
console.log(err)
})

View File

@ -0,0 +1,535 @@
<template>
<div class="app-container">
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" fixed label="电话" width="120" prop="mobile" />
<el-table-column align="center" fixed label="平台" width="80" prop="os_name" />
<el-table-column align="center" fixed label="直播" width="60">
<template slot-scope="scope">
<el-tag v-if="scope.row.is_zhibo"></el-tag>
<el-tag v-else type="info"></el-tag>
</template>
</el-table-column>
<el-table-column align="center" fixed label="客服" width="80" prop="admin.username" />
<el-table-column align="center" label="订单号" width="180" prop="sn" />
<el-table-column width="138px" align="center" label="下单时间">
<template slot-scope="scope">
<span>{{ scope.row.create_at | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column width="160px" align="center" label="派单时间">
<template slot-scope="scope">
<span>{{ scope.row.give_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="状态" width="80">
<template slot-scope="scope">
<div style="padding: 1px 5px; border-radius: 3px;" :style="{color:order_status[scope.row.order_status],border:`1px solid ${order_status[scope.row.order_status]}`}" type="primary">{{ scope.row.order_status_name }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="跟进状态" width="90">
<template slot-scope="scope">
<div style="padding: 1px 5px; border-radius: 3px;" :style="{color:follow_status[scope.row.status],border:`1px solid ${follow_status[scope.row.status]}`}" type="primary">{{ scope.row.status_name }}</div>
</template>
</el-table-column>
<el-table-column align="center" width="500px" label="标题" prop="product_name" />
<el-table-column width="500px" align="center" label="跟进备注" prop="remark" />
<el-table-column align="center" label="联系人" width="120" prop="contact" />
<!-- <el-table-column align="center" label="微信" width="80">
<template slot-scope="scope">
<i v-if="scope.row.is_wechat>0" class="el-icon-circle-check"></i>
</template>
</el-table-column> -->
<el-table-column width="138px" align="center" label="出行时间">
<template slot-scope="scope">
<span>{{ scope.row.travel_date | parseTime('{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column width="138px" align="center" label="最后跟进时间">
<template slot-scope="scope">
<span>{{ scope.row.last_follow | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="核单" width="80">
<template slot-scope="scope">
<i v-if="scope.row.is_check == 1" class="el-icon-check" />
<i v-if="scope.row.is_check == 2" class="el-icon-close" />
</template>
</el-table-column>
<el-table-column align="center" width="138px" label="分类" prop="category_desc" />
<el-table-column align="center" label="总金额" width="120">
<template slot-scope="scope">
<span>{{ scope.row.total_price/100 }}</span>
</template>
</el-table-column>
<el-table-column align="center" width="80px" label="人数" prop="quantity" />
<el-table-column align="center" label="主播" width="80" prop="anchor.username" />
<el-table-column width="138px" align="center" label="修改时间">
<template slot-scope="scope">
<span>{{ scope.row.update_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList"
/>
<el-dialog title="订单跟进" :visible.sync="dialogVisible">
<el-form label-width="130px" :model="item">
<el-form-item label="产品名称">
{{ item.product_name }}
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="产品状态">
{{ item.order_status_name }}
</el-form-item>
<el-form-item label="数量">
{{ item.quantity }}
</el-form-item>
<el-form-item label="联系人">
{{ item.contact }}
</el-form-item>
<el-form-item label="手机">
{{ item.mobile }}
</el-form-item>
<el-form-item label="下单时间">
{{ item.create_at | parseTime('{y}-{m}-{d} {h}:{i}') }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="人员">
<el-row>
<el-col :span="3">大人</el-col>
<el-col :span="5"><el-input v-model="item.personnel.adult" name="adult" placeholder="大人" /></el-col>
<el-col :span="3">老人</el-col>
<el-col :span="5"><el-input v-model="item.personnel.old" name="old" placeholder="老人" /></el-col>
<el-col :span="3">小孩</el-col>
<el-col :span="5"><el-input v-model="item.personnel.child" name="child" placeholder="小孩" /></el-col>
</el-row>
</el-form-item>
<el-form-item v-if="item.status!==1" label="核销码">
<el-input v-model="item.check_sn" name="check_sn" placeholder="请输入平台核销码" />
</el-form-item>
<el-form-item label="加微信" v-if="item.status!==2">
<el-checkbox v-model="item.is_wechat" :true-label="1" :false-label="0">已加微信</el-checkbox>
</el-form-item>
<el-form-item label="出游日期">
<el-date-picker
v-model="item.travel_date"
type="date"
placeholder="选择日期时间"
/>
</el-form-item>
<el-form-item v-if="item.status!==1" label="返回日期">
<el-date-picker
v-model="item.travel_end"
type="date"
placeholder="选择日期时间"
/>
</el-form-item>
<el-form-item label="下次跟进时间" v-if="item.status!==2">
<el-date-picker
v-model="next_follow"
type="datetime"
placeholder="选择日期时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="跟进状态">
<template v-for="(v,k) in status_arr">
<el-radio v-if="k > 0" v-model="item.status" :label="k" border>{{ v }}</el-radio>
</template>
</el-form-item>
<!-- <el-form-item label="快捷跟进" style="width: 600px;">
<el-select v-model="value" placeholder="请选择" @change="onChange">
<el-form-item style="display: inline-flex;text-align: left;width: 770px;">
<el-option
v-for="item in options"
:key="item.value"
style="width: 250px;display: inline-flex;word-break: break-all;"
:label="item.label"
:value="item.label"
/>
</el-form-item>
</el-select>
</el-form-item>-->
<el-form-item label="跟进说明">
<el-input v-model="item.desc" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="onSave(item)"> </el-button>
</div>
<el-tabs v-model="active" type="border-card">
<el-tab-pane name="follow" label="跟进记录">
<el-table
:data="item.follow"
style="width: 100%"
>
<el-table-column
label="日期"
width="138"
>
<template slot-scope="scope">
<span>{{ scope.row.create_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column
label="跟进人"
width="110"
prop="name"
/>
<el-table-column
label="状态"
width="80"
>
<template slot-scope="scope">
<span>{{ status_arr[scope.row.status] }}</span>
</template>
</el-table-column>
<el-table-column
prop="desc"
label="跟进说明"
/>
</el-table>
</el-tab-pane>
<el-tab-pane name="finance" label="财务记录">
<el-table
:data="item.finance"
style="width: 100%"
>
<el-table-column
label="日期"
>
<template slot-scope="scope">
<span>{{ scope.row.create_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column
label="类型"
width="110"
>
<template slot-scope="scope">
<span>{{ type_arr[scope.row.type] }}</span>
</template>
</el-table-column>
<el-table-column
label="状态"
width="120"
>
<template slot-scope="scope">
<span>{{ scope.row.total/100 }}</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-dialog>
<el-dialog title="纯核销" :visible.sync="dialog2Visible">
<el-form label-width="160px" :model="form">
<el-form-item label="平台">
<el-radio v-model="form.os" label="1">美团</el-radio>
</el-form-item>
<el-form-item label="核销码">
<el-input v-model="form.check_sn" placeholder="请输入平台核销码" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="onPass(form)"> </el-button>
</div>
</el-dialog>
<el-dialog title="申请转出订单" :visible.sync="applyVisible">
<el-form label-width="160px" :model="item3" :rules="rules" ref="ruleForm">
<el-form-item label="标题:">
<el-input v-model="item3.product_name" disabled />
</el-form-item>
<el-form-item label="订单号:">
<el-input v-model="item3.sn" disabled />
</el-form-item>
<el-form-item label="流转对象:" style="width: 600px;" prop="flowObj">
<el-select v-model="item3.flowObj" placeholder="请选择" @change="onChange2">
<el-form-item style="display: inline-flex;text-align: left;width: 770px;">
<el-option
v-for="item in adminList"
:key="item.value"
style="width: 250px;display: inline-flex;word-break: break-all;"
:label="item.username"
:value="item.id"
/>
</el-form-item>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<!-- scope.row.backs&&scope.row.backs.status==2? -->
<el-button v-if="item3.backs&&item3.backs.status==0" type="primary" @click="onCancel(item3.flowObj)"> </el-button>
<el-button v-else type="primary" @click="onCirculationSave(item3.flowObj)"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
// import Pagination from '@/components/Pagination'
import Pagination from '@/components/PaginationFixed'
import {orderBack} from '@/api/order'
export default {
name: 'Orderlist',
components: { Pagination },
data() {
return {
active: 'follow',
types: { 0: '', 1: '', 2: '', 3: 'primary', 4: 'success', 5: 'warning', 6: 'danger', 7: 'info' },
types2: { 1: 'primary', 2: 'success', 3: 'warning' },
status_arr: ['待跟进', '跟进中', '已核销', '核销失败', '放弃跟单'],
type_arr: ['-', '收益', '支出'],
timetype_arr: {},
order_status: ['#9e9f9c', '#04bcd9', '#fc9904', '#1193f4', '#48b14b', '#eb1662', '#9d1cb5'],
follow_status: ['#9e9f9c', '#04bcd9', '#fc9904', '#1193f4', '#48b14b', '#eb1662'],
options: [],
value: null,
next_follow: null,
list: [],
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 10,
times: [],
status: null,
admin: null,
zhubo: null,
os_status:[]
},
item: { next_follow: '', personnel: {}},
follow: [],
dialogVisible: false,
dialog2Visible: false,
applyVisible: false,
oss: [],
item3: {
sn: null,
backs:null,
flowObj:'',
os: null // 12 3
},
os_arr: { 1: '美团', 2: '快手', 3: '抖音' },
adminList: [],
form: {},
rules:{
flowObj: [
{ required: true, message: '请选择流转对象', trigger: 'change' }
],
}
}
},
created() {
// this.listQuery.status = this.$route.query.status || null
this.listQuery.zhubo = this.$route.query.zhubo || null
if (this.$route.query.start && this.$route.query.end) {
this.listQuery.times = [this.$route.query.start, this.$route.query.end]
}
this.setQuery('status')
this.setQuery('os_status')
this.setQuery('times')
this.getList()
this.getShortcutContent()
this.getAdminList()
},
methods: {
setQuery(key){
if (this.$route.query.hasOwnProperty(key)) {
this.listQuery[key] = this.$route.query[key]
} else {
this.listQuery[key] = ''
}
},
getList($is_excel) {
this.listQuery.excel = null
if ($is_excel == 1) {
this.listQuery.excel = 1
const isdate = this.listQuery.times[0] instanceof Date
const params = {
...this.listQuery,
times: [isdate ? this.listQuery.times[0].toISOString() : '', isdate ? this.listQuery.times[1].toISOString() : '']
}
window.open('/admin/order/index?' + this.objectToQuery(params))
return
}
this.listQuery.status = 4 //todo
this.$axios.get('/admin/order/index', { params: this.listQuery }).then(response => {
this.list = response.data.data
this.total = response.data.total
this.timetype_arr = response.ext.timetype,
this.oss = response.ext.oss
this.listLoading = false
})
},
objectToQuery(obj) {
return Object.keys(obj).map(key => {
const value = obj[key]
if (value == undefined || value == null) return ''
return encodeURIComponent(key) + '=' + encodeURIComponent(value)
}).join('&')
},
onInfo(item) {
this.value = null
this.next_follow = null
this.$set(item, 'next_follow', null)
this.item = item
this.active = 'follow'
this.$axios.get('/admin/order/info', { params: { id: item.id }}).then(res => {
this.item = res.data
this.dialogVisible = true
}).catch(err => {
})
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
getAdminList() {
this.$axios.get('/admin/admin/index', { params: { limit: 100, status: 1, is_order: 1 }}).then(response => {
this.adminList = response.data.data
this.listLoading = false
}).catch(err => {
})
},
onCirculation(item) {
this.applyVisible = true
this.item3 = { ...item, os: Number(item.os) }
console.log(this.item3);
if(this.item3.backs&&this.item3.backs.admin_id){
this.item3.flowObj = this.item3.backs.admin_id
}else{
this.resetForm('ruleForm')
}
},
//
onCirculationSave(to_admin_id) {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
orderBack({ sn: this.item3.sn, os: this.item3.os, to_admin_id: to_admin_id }).then((res)=>{
this.applyVisible = false
this.getList()
})
} else {
return false;
}
});
},
//
onCancel() {
this.$refs.ruleForm.validate((valid) => {
if(valid){
this.$axios.post('/admin/order/backcancel', { id: this.item3.id }).then(res => {
this.applyVisible = false
this.getList()
}).catch(err => {
console.log(err)
})
}else{
return false
}
});
},
onBack() {
this.$axios.post('/admin/order/back', this.item).then(res => {
this.dialogVisible = false
this.item = {}
this.getList()
}).catch(err => {
})
},
onSave(item) {
console.log(this.next_follow)
this.$axios.post('/admin/order/save', { id: item.id, check_sn: item.check_sn, is_wechat: item.is_wechat, travel_end: item.travel_end, travel_date: item.travel_date, desc: item.desc, status: item.status, next_follow: this.next_follow, personnel: this.item.personnel }).then(res => {
this.dialogVisible = false
this.item = { next_follow: '', personnel: {}}
}).catch(err => {
})
},
onPass(form) {
this.$axios.post('/admin/order/pass', { check_sn: form.check_sn }).then(res => {
this.dialog2Visible = false
this.form = {}
}).catch(err => {
})
},
onChange(from) {
this.$set(this.item, 'desc', from + (this.item.desc != undefined ? this.item.desc : ''))
},
onChange2(from) {
this.$set(this.item, 'to_admin_id', from + (this.item.admin_id != undefined ? this.item.admin_id : ''))
},
handleChange(os) {
console.log(os)
},
getShortcutContent() {
this.listLoading = true
this.$axios.get('/admin/shortcutContent/list', { params: { page: 1, limit: 50, status: 1 }}).then(response => {
for (const r of response.data.data) {
this.options.push({ value: r.id, label: r.content })
}
}).catch(() => {
})
}
}
}
</script>
<style scoped>
.app-container {
position: relative;
padding-bottom: 60px; /* 分页条的高度 */
}
.filter-container,
.el-table {
padding-bottom: 52px; /* 分页条的高度,以避免内容重叠 */
}
</style>

View File

@ -0,0 +1,538 @@
<template>
<div class="app-container">
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" fixed label="电话" width="120" prop="mobile" />
<el-table-column align="center" fixed label="平台" width="80" prop="os_name" />
<el-table-column align="center" fixed label="直播" width="60">
<template slot-scope="scope">
<el-tag v-if="scope.row.is_zhibo"></el-tag>
<el-tag v-else type="info"></el-tag>
</template>
</el-table-column>
<el-table-column align="center" fixed label="客服" width="80" prop="admin.username" />
<el-table-column align="center" label="订单号" width="180" prop="sn" />
<el-table-column width="138px" align="center" label="下单时间">
<template slot-scope="scope">
<span>{{ scope.row.create_at | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column width="160px" align="center" label="派单时间">
<template slot-scope="scope">
<span>{{ scope.row.give_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="状态" width="80">
<template slot-scope="scope">
<div style="padding: 1px 5px; border-radius: 3px;" :style="{color:order_status[scope.row.order_status],border:`1px solid ${order_status[scope.row.order_status]}`}" type="primary">{{ scope.row.order_status_name }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="跟进状态" width="90">
<template slot-scope="scope">
<div style="padding: 1px 5px; border-radius: 3px;" :style="{color:follow_status[scope.row.status],border:`1px solid ${follow_status[scope.row.status]}`}" type="primary">{{ scope.row.status_name }}</div>
</template>
</el-table-column>
<el-table-column align="center" width="500px" label="标题" prop="product_name" />
<el-table-column width="500px" align="center" label="跟进备注" prop="remark" />
<el-table-column align="center" label="联系人" width="120" prop="contact" />
<!-- <el-table-column align="center" label="微信" width="80">
<template slot-scope="scope">
<i v-if="scope.row.is_wechat>0" class="el-icon-circle-check"></i>
</template>
</el-table-column> -->
<el-table-column width="138px" align="center" label="出行时间">
<template slot-scope="scope">
<span>{{ scope.row.travel_date | parseTime('{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column width="138px" align="center" label="最后跟进时间">
<template slot-scope="scope">
<span>{{ scope.row.last_follow | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="核单" width="80">
<template slot-scope="scope">
<i v-if="scope.row.is_check == 1" class="el-icon-check" />
<i v-if="scope.row.is_check == 2" class="el-icon-close" />
</template>
</el-table-column>
<el-table-column align="center" width="138px" label="分类" prop="category_desc" />
<el-table-column align="center" label="总金额" width="120">
<template slot-scope="scope">
<span>{{ scope.row.total_price/100 }}</span>
</template>
</el-table-column>
<el-table-column align="center" width="80px" label="人数" prop="quantity" />
<el-table-column align="center" label="主播" width="80" prop="anchor.username" />
<el-table-column width="138px" align="center" label="修改时间">
<template slot-scope="scope">
<span>{{ scope.row.update_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList"
/>
<el-dialog title="订单跟进" :visible.sync="dialogVisible">
<el-form label-width="130px" :model="item">
<el-form-item label="产品名称">
{{ item.product_name }}
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="产品状态">
{{ item.order_status_name }}
</el-form-item>
<el-form-item label="数量">
{{ item.quantity }}
</el-form-item>
<el-form-item label="联系人">
{{ item.contact }}
</el-form-item>
<el-form-item label="手机">
{{ item.mobile }}
</el-form-item>
<el-form-item label="下单时间">
{{ item.create_at | parseTime('{y}-{m}-{d} {h}:{i}') }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="人员">
<el-row>
<el-col :span="3">大人</el-col>
<el-col :span="5"><el-input v-model="item.personnel.adult" name="adult" placeholder="大人" /></el-col>
<el-col :span="3">老人</el-col>
<el-col :span="5"><el-input v-model="item.personnel.old" name="old" placeholder="老人" /></el-col>
<el-col :span="3">小孩</el-col>
<el-col :span="5"><el-input v-model="item.personnel.child" name="child" placeholder="小孩" /></el-col>
</el-row>
</el-form-item>
<el-form-item v-if="item.status!==1" label="核销码">
<el-input v-model="item.check_sn" name="check_sn" placeholder="请输入平台核销码" />
</el-form-item>
<el-form-item label="加微信" v-if="item.status!==2">
<el-checkbox v-model="item.is_wechat" :true-label="1" :false-label="0">已加微信</el-checkbox>
</el-form-item>
<el-form-item label="出游日期">
<el-date-picker
v-model="item.travel_date"
type="date"
placeholder="选择日期时间"
/>
</el-form-item>
<el-form-item v-if="item.status!==1" label="返回日期">
<el-date-picker
v-model="item.travel_end"
type="date"
placeholder="选择日期时间"
/>
</el-form-item>
<el-form-item label="下次跟进时间" v-if="item.status!==2">
<el-date-picker
v-model="next_follow"
type="datetime"
placeholder="选择日期时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="跟进状态">
<template v-for="(v,k) in status_arr">
<el-radio v-if="k > 0" v-model="item.status" :label="k" border>{{ v }}</el-radio>
</template>
</el-form-item>
<!-- <el-form-item label="快捷跟进" style="width: 600px;">
<el-select v-model="value" placeholder="请选择" @change="onChange">
<el-form-item style="display: inline-flex;text-align: left;width: 770px;">
<el-option
v-for="item in options"
:key="item.value"
style="width: 250px;display: inline-flex;word-break: break-all;"
:label="item.label"
:value="item.label"
/>
</el-form-item>
</el-select>
</el-form-item>-->
<el-form-item label="跟进说明">
<el-input v-model="item.desc" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="onSave(item)"> </el-button>
</div>
<el-tabs v-model="active" type="border-card">
<el-tab-pane name="follow" label="跟进记录">
<el-table
:data="item.follow"
style="width: 100%"
>
<el-table-column
label="日期"
width="138"
>
<template slot-scope="scope">
<span>{{ scope.row.create_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column
label="跟进人"
width="110"
prop="name"
/>
<el-table-column
label="状态"
width="80"
>
<template slot-scope="scope">
<span>{{ status_arr[scope.row.status] }}</span>
</template>
</el-table-column>
<el-table-column
prop="desc"
label="跟进说明"
/>
</el-table>
</el-tab-pane>
<el-tab-pane name="finance" label="财务记录">
<el-table
:data="item.finance"
style="width: 100%"
>
<el-table-column
label="日期"
>
<template slot-scope="scope">
<span>{{ scope.row.create_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column
label="类型"
width="110"
>
<template slot-scope="scope">
<span>{{ type_arr[scope.row.type] }}</span>
</template>
</el-table-column>
<el-table-column
label="状态"
width="120"
>
<template slot-scope="scope">
<span>{{ scope.row.total/100 }}</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-dialog>
<el-dialog title="纯核销" :visible.sync="dialog2Visible">
<el-form label-width="160px" :model="form">
<el-form-item label="平台">
<el-radio v-model="form.os" label="1">美团</el-radio>
</el-form-item>
<el-form-item label="核销码">
<el-input v-model="form.check_sn" placeholder="请输入平台核销码" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="onPass(form)"> </el-button>
</div>
</el-dialog>
<el-dialog title="申请转出订单" :visible.sync="applyVisible">
<el-form label-width="160px" :model="item3" :rules="rules" ref="ruleForm">
<el-form-item label="标题:">
<el-input v-model="item3.product_name" disabled />
</el-form-item>
<el-form-item label="订单号:">
<el-input v-model="item3.sn" disabled />
</el-form-item>
<el-form-item label="流转对象:" style="width: 600px;" prop="flowObj">
<el-select v-model="item3.flowObj" placeholder="请选择" @change="onChange2">
<el-form-item style="display: inline-flex;text-align: left;width: 770px;">
<el-option
v-for="item in adminList"
:key="item.value"
style="width: 250px;display: inline-flex;word-break: break-all;"
:label="item.username"
:value="item.id"
/>
</el-form-item>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<!-- scope.row.backs&&scope.row.backs.status==2? -->
<el-button v-if="item3.backs&&item3.backs.status==0" type="primary" @click="onCancel(item3.flowObj)"> </el-button>
<el-button v-else type="primary" @click="onCirculationSave(item3.flowObj)"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
// import Pagination from '@/components/Pagination'
import Pagination from '@/components/PaginationFixed'
import {orderBack} from '@/api/order'
export default {
name: 'Orderlist',
components: { Pagination },
data() {
return {
active: 'follow',
types: { 0: '', 1: '', 2: '', 3: 'primary', 4: 'success', 5: 'warning', 6: 'danger', 7: 'info' },
types2: { 1: 'primary', 2: 'success', 3: 'warning' },
status_arr: ['待跟进', '跟进中', '已核销', '核销失败', '放弃跟单'],
type_arr: ['-', '收益', '支出'],
timetype_arr: {},
order_status: ['#9e9f9c', '#04bcd9', '#fc9904', '#1193f4', '#48b14b', '#eb1662', '#9d1cb5'],
follow_status: ['#9e9f9c', '#04bcd9', '#fc9904', '#1193f4', '#48b14b', '#eb1662'],
options: [],
value: null,
next_follow: null,
list: [],
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 10,
times: [],
status: null,
admin: null,
zhubo: null,
os_status:[]
},
item: { next_follow: '', personnel: {}},
follow: [],
dialogVisible: false,
dialog2Visible: false,
applyVisible: false,
oss: [],
item3: {
sn: null,
backs:null,
flowObj:'',
os: null // 12 3
},
os_arr: { 1: '美团', 2: '快手', 3: '抖音' },
adminList: [],
form: {},
rules:{
flowObj: [
{ required: true, message: '请选择流转对象', trigger: 'change' }
],
}
}
},
created() {
// this.listQuery.status = this.$route.query.status || null
this.listQuery.zhubo = this.$route.query.zhubo || null
if (this.$route.query.start && this.$route.query.end) {
this.listQuery.times = [this.$route.query.start, this.$route.query.end]
}
this.setQuery('status')
this.setQuery('os_status')
this.setQuery('times')
this.getList()
this.getShortcutContent()
this.getAdminList()
},
methods: {
setQuery(key){
if (this.$route.query.hasOwnProperty(key)) {
this.listQuery[key] = this.$route.query[key]
} else {
this.listQuery[key] = ''
}
},
getList($is_excel) {
this.listQuery.excel = null
if ($is_excel == 1) {
this.listQuery.excel = 1
const isdate = this.listQuery.times[0] instanceof Date
const params = {
...this.listQuery,
times: [isdate ? this.listQuery.times[0].toISOString() : '', isdate ? this.listQuery.times[1].toISOString() : '']
}
window.open('/admin/order/index?' + this.objectToQuery(params))
return
}
this.listQuery.os_status = [4,2] //todo 使
this.$axios.get('/admin/order/index', { params: this.listQuery }).then(response => {
this.list = response.data.data
this.total = response.data.total
this.timetype_arr = response.ext.timetype,
this.oss = response.ext.oss
this.listLoading = false
})
},
objectToQuery(obj) {
return Object.keys(obj).map(key => {
const value = obj[key]
if (value == undefined || value == null) return ''
return encodeURIComponent(key) + '=' + encodeURIComponent(value)
}).join('&')
},
onInfo(item) {
this.value = null
this.next_follow = null
this.$set(item, 'next_follow', null)
this.item = item
this.active = 'follow'
this.$axios.get('/admin/order/info', { params: { id: item.id }}).then(res => {
this.item = res.data
this.dialogVisible = true
}).catch(err => {
})
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
getAdminList() {
this.$axios.get('/admin/admin/index', { params: { limit: 100, status: 1, is_order: 1 }}).then(response => {
this.adminList = response.data.data
this.listLoading = false
}).catch(err => {
})
},
onCirculation(item) {
this.applyVisible = true
this.item3 = { ...item, os: Number(item.os) }
console.log(this.item3);
if(this.item3.backs&&this.item3.backs.admin_id){
this.item3.flowObj = this.item3.backs.admin_id
}else{
this.resetForm('ruleForm')
}
},
//
onCirculationSave(to_admin_id) {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
orderBack({ sn: this.item3.sn, os: this.item3.os, to_admin_id: to_admin_id }).then((res)=>{
this.applyVisible = false
this.getList()
})
} else {
return false;
}
});
},
//
onCancel() {
this.$refs.ruleForm.validate((valid) => {
if(valid){
this.$axios.post('/admin/order/backcancel', { id: this.item3.id }).then(res => {
this.applyVisible = false
this.getList()
}).catch(err => {
console.log(err)
})
}else{
return false
}
});
},
onBack() {
this.$axios.post('/admin/order/back', this.item).then(res => {
this.dialogVisible = false
this.item = {}
this.getList()
}).catch(err => {
})
},
onSave(item) {
console.log(this.next_follow)
this.$axios.post('/admin/order/save', { id: item.id, check_sn: item.check_sn, is_wechat: item.is_wechat, travel_end: item.travel_end, travel_date: item.travel_date, desc: item.desc, status: item.status, next_follow: this.next_follow, personnel: this.item.personnel }).then(res => {
this.dialogVisible = false
this.item = { next_follow: '', personnel: {}}
}).catch(err => {
})
},
onPass(form) {
this.$axios.post('/admin/order/pass', { check_sn: form.check_sn }).then(res => {
this.dialog2Visible = false
this.form = {}
}).catch(err => {
})
},
onChange(from) {
this.$set(this.item, 'desc', from + (this.item.desc != undefined ? this.item.desc : ''))
},
onChange2(from) {
this.$set(this.item, 'to_admin_id', from + (this.item.admin_id != undefined ? this.item.admin_id : ''))
},
handleChange(os) {
console.log(os)
},
getShortcutContent() {
this.listLoading = true
this.$axios.get('/admin/shortcutContent/list', { params: { page: 1, limit: 50, status: 1 }}).then(response => {
for (const r of response.data.data) {
this.options.push({ value: r.id, label: r.content })
}
}).catch(() => {
})
}
}
}
</script>
<style scoped>
.app-container {
position: relative;
padding-bottom: 60px; /* 分页条的高度 */
}
.filter-container,
.el-table {
padding-bottom: 52px; /* 分页条的高度,以避免内容重叠 */
}
</style>

View File

@ -7,6 +7,7 @@ use app\model\Backs;
use app\model\Finances;
use app\model\Onlines;
use app\model\Orders;
use DateTime;
use Qiniu\Auth;
use support\Log;
use support\Redis;
@ -90,25 +91,69 @@ class IndexController extends base
public function orders(Request $request)
{
$order = Orders::where('sn','>',0)
// 获取当天结束时间23:59:59
$currentTime = new DateTime();
$currentTime->setTime(23, 59, 59);
$endOfDayTimestamp = $currentTime->getTimestamp() * 1000;
// 获取前30天的开始时间00:00:00
$thirtyDaysAgo = new DateTime('-30 days');
$thirtyDaysAgo->setTime(0, 0, 0);
$startOfThirtyDaysAgoTimestamp = $thirtyDaysAgo->getTimestamp() * 1000;
// 获取前60天的开始时间00:00:00
$sixtyDaysAgo = new DateTime('-60 days');
$sixtyDaysAgo->setTime(0, 0, 0);
$startOfSixtyDaysAgoTimestamp = $sixtyDaysAgo->getTimestamp() * 1000;
// 获取前80天的开始时间00:00:00
$eightyDaysAgo = new DateTime('-80 days');
$eightyDaysAgo->setTime(0, 0, 0);
$startOfEightyDaysAgoTimestamp = $eightyDaysAgo->getTimestamp() * 1000;
$order = Orders::where('sn', '>', 0)
->fieldRaw('
SUM(IF(status=0,1,0)) as wait,
SUM(IF(status=1,1,0)) as doing,
SUM(total_price) as total,
count(id) as `all`,
COUNT(id) as `all`,
SUM(IF(asset_price>0 AND status=2,1,0)) as asset,
SUM(IF(asset_price>0 AND status=2,asset_price,0)) as asset_price,
SUM(CASE
WHEN (os = 1 AND STATUS = 5) OR (os = 3 AND STATUS = 4) OR (os = 2 AND STATUS = 1) THEN 1
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) THEN 1
ELSE 0
END) AS refund,
SUM(CASE
WHEN (os = 1 AND STATUS = 5) OR (os = 3 AND STATUS = 4) OR (os = 2 AND STATUS = 1) THEN actual_price
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) THEN actual_price
ELSE 0
END) AS refund_price
END) AS refund_price,
SUM(CASE
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) AND create_at >= ' . $startOfThirtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
ELSE 0
END) AS refund_30,
SUM(CASE
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) AND create_at >= ' . $startOfThirtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN actual_price
ELSE 0
END) AS refund_price_30,
SUM(CASE
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) AND create_at >= ' . $startOfSixtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
ELSE 0
END) AS refund_60,
SUM(CASE
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) AND create_at >= ' . $startOfSixtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN actual_price
ELSE 0
END) AS refund_price_60,
SUM(CASE
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) AND create_at >= ' . $startOfEightyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
ELSE 0
END) AS refund_80,
SUM(CASE
WHEN ((os = 1 AND status = 5) OR (os = 3 AND status = 4) OR (os = 2 AND status = 1)) AND create_at >= ' . $startOfEightyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN actual_price
ELSE 0
END) AS refund_price_80
');
if ($request->admin->id !=1){
if ($request->admin->id != 1) {
$order = $order->where('admin_id', $request->admin->id);
}
@ -123,15 +168,16 @@ class IndexController extends base
'asset_price' => $order->asset_price ?? 0,
'refund' => $order->refund ?? 0,
'refund_price' => $order->refund_price ?? 0,
'refund_30' => $list->refund_30 ?? 0,
'refund_price_30' => $list->refund_price_30 ?? 0.00,
'refund_60' => $list->refund_60 ?? 0,
'refund_price_60' => $list->refund_price_60 ?? 0.00,
'refund_80' => $list->refund_80 ?? 0,
'refund_price_80' => $list->refund_price_80 ?? 0.00
'refund_30' => $order->refund_30 ?? 0,
'refund_price_30' => $order->refund_price_30 ?? 0.00,
'refund_60' => $order->refund_60 ?? 0,
'refund_price_60' => $order->refund_price_60 ?? 0.00,
'refund_80' => $order->refund_80 ?? 0,
'refund_price_80' => $order->refund_price_80 ?? 0.00
]);
}
public function pie()
{
$time = strtotime(date('Y-m-d'));

View File

@ -38,12 +38,16 @@ class OrderController extends base
->order('update_time','desc')
->order('id','desc');
if($status!=null && $status >= 0) {
$query->where('status', $status);
if($os_status) {
if ($os_status[0] == array_search(Orders::OSS[4],Orders::OSS)){
$query->whereRaw(Orders::AllOssStatusSql[$os_status[1]]);
}else{
$query->where('os', $os_status[0] ?? 0 )->where('order_status', $os_status[1] ?? 0);
}
}
if($os_status) {
$query->where('os', $os_status[0] ?? 0 )->where('order_status', $os_status[1] ?? 0);
if($status!=null && $status >= 0) {
$query->where('status', $status);
}
$mobile = $request->get('mobile');
@ -114,9 +118,9 @@ class OrderController extends base
$statss = Orders::KuaishouStatus;
}elseif($key ==3) {
$statss = Orders::DouyinStatus;
}/*elseif ($key == 4) {
$statss = Orders::AllPlatform;
}*/
}elseif ($key == 4) {
$statss = Orders::AllOssStatus;
}
$_ch = [];
foreach($statss as $k=>$v) {

View File

@ -3,9 +3,11 @@
namespace app\admin\controller;
use app\model\QaCitys;
use app\model\QaQuestions;
use app\model\Qas;
use support\Log;
use support\Request;
use think\facade\Db;
class QaController extends base
{
@ -14,7 +16,7 @@ class QaController extends base
$city_id = $request->get('city_id');
$keyword = $request->get('keyword');
$list = Qas::order('id decs');
$list = Qas::with('qaQuestions')->order('id decs');
if (!empty($city_id)){
$list = $list->where(['status' => 1, 'city_id' => $city_id]);
@ -38,7 +40,7 @@ class QaController extends base
$title = $request->get('title');
$status = $request->get('status');
$list = Qas::with('qaCitys')->order('create_time desc');
$list = Qas::with(['qaCitys','qaQuestions'])->order('create_time desc');
if (!empty($title)){
$list = $list->where('title','like','%'.$title.'%');
@ -56,7 +58,7 @@ class QaController extends base
$id = $request->get('id');
if (empty($id)) return $this->error(2001, 'id data cannot be empty!');
$data = Qas::where('status',1)->find();
$data = Qas::with(['qaCitys','qaQuestions'])->where('status',1)->find();
return $this->success($data);
}
@ -66,29 +68,60 @@ class QaController extends base
$post = $request->post();
if (empty($post['city_id'])) return $this->error(2001, 'city_id data cannot be empty!');
if (empty($post['title'])) return $this->error(2001, 'title data cannot be empty!');
if (empty($post['content'])) return $this->error(2001, 'content data cannot be empty!');
$qaQuestion = $post['qaQuestions'];
unset($post['qaQuestions']);
try {
Db::transaction(function () use ($post,$qaQuestion){
$data = Qas::create($post);
if (!empty($qaQuestion)){
foreach ($qaQuestion as $k => $v){
$qaQuestion = new QaQuestions();
$qaQuestion->qa_id = $data->id;
$qaQuestion->title = $v['title'];
$qaQuestion->content = $v['content'];
$qaQuestion->sort = $v['sort'];
$qaQuestion->save();
}
$data->qaQuestions = $qaQuestion;
}
});
} catch (\Exception $e) {
return $this->error(2002, $e->getMessage());
}
return $this->success($data);
return $this->success(null);
}
public function editQa(Request $request)
{
$post = $request->post();
if (empty($post['id'])) return $this->error(2001, 'id data cannot be empty!');
$qaQuestion = $post['qaQuestions'];
unset($post['create_time']);
unset($post['update_time']);
unset($post['qaQuestions']);
try {
Db::transaction(function () use ($post,$qaQuestion){
$data = Qas::update($post);
if (!empty($qaQuestion)){
QaQuestions::where('qa_id',$post['id'])->delete();
foreach ($qaQuestion as $k => $v){
$qaQuestion = new QaQuestions();
$qaQuestion->qa_id = $data->id;
$qaQuestion->title = $v['title'];
$qaQuestion->content = $v['content'];
$qaQuestion->sort = $v['sort'];
$qaQuestion->save();
}
$data->qa_question = $qaQuestion;
}
});
} catch (\Exception $e) {
return $this->error(2002, $e->getMessage());
}
return $this->success($data);
return $this->success(null);
}
public function delQa(Request $request)

View File

@ -16,7 +16,13 @@ class Orders extends base
const DouyinReservationStatus = [1 => '未预约', 2 => '待接单', 3 => '已预约', 4 => '已取消', 5 => '已完成', 6 => '取消/退款申请'];
const DouyinStatus = [1 => '未核销', 2 => '已核销', 3 => '申请退款中', 4 => '已退款', 5 => '部分核销'];
const AllPlatform = [1 => '待使用', 2 => '已核销', 3 => '已退款'];
const AllOssStatus = [1 => '待使用', 2 => '已核销', 3 => '已退款'];
const AllOssStatusSql = [
1 => '((os=1 and order_status=3) or (os=2 and order_status=4) or (os=3 and order_status=1))', //待使用
2 => '((os=1 and order_status=4) or (os=2 and order_status=5) or (os=3 and order_status=2))', //已核销
3 => '((os=1 and order_status=5) or (os=3 and order_status=4))' //已退款(快手暂无已退款状态)
];
const StatusName = ['待跟进', '跟进中', '已核销', '核销失败', '放弃跟单'];
const OSS = [1 => '美团', 2 => '快手', 3 => '抖音', 4 => '全平台'];

View File

@ -0,0 +1,8 @@
<?php
namespace app\model;
class QaQuestions extends base
{
}

View File

@ -10,4 +10,9 @@ class Qas extends base
return $this->hasOne(QaCitys::class,'city_id','city_id');
}
public function qaQuestions()
{
return $this->hasMany(QaQuestions::class,'qa_id','id')->order('sort desc');
}
}