Merge branch 'main' of https://code.tiantongsl.com/senyao/travel
This commit is contained in:
commit
e937d047ce
|
@ -15,6 +15,12 @@
|
|||
"test:ci": "npm run lint && npm run test:unit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fullcalendar/core": "^6.1.15",
|
||||
"@fullcalendar/daygrid": "^6.1.15",
|
||||
"@fullcalendar/interaction": "^6.1.15",
|
||||
"@fullcalendar/list": "^6.1.15",
|
||||
"@fullcalendar/timegrid": "^6.1.15",
|
||||
"@fullcalendar/vue": "^6.1.15",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^1.0.2",
|
||||
"axios": "0.18.1",
|
||||
|
@ -31,6 +37,7 @@
|
|||
"jsonlint": "1.6.3",
|
||||
"jszip": "3.2.1",
|
||||
"left-pad": "^1.3.0",
|
||||
"moment": "^2.30.1",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "2.4.0",
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import request from '@/utils/request'
|
||||
// 获取路线列表
|
||||
export function getProductsList() {
|
||||
return request({
|
||||
url: 'admin/products/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addProducts(data) {
|
||||
return request({
|
||||
url: '/admin/products/add',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
|
@ -7,6 +7,13 @@ export function orderBack(data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
export function orderbackBatch(data) {
|
||||
return request({
|
||||
url: 'admin/order/backBatch',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 获取qa详情
|
||||
export function getQaDetail(city_id) {
|
||||
return request({
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
//直播间列表
|
||||
export function liveroom() {
|
||||
return request({
|
||||
url: '/admin/liveroom/index',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 排班列表
|
||||
export function roomWorks(live_room_id) {
|
||||
return request({
|
||||
url: '/admin/liveroom/roomWorks',
|
||||
method: 'get',
|
||||
params: { live_room_id }
|
||||
})
|
||||
}
|
||||
// 可排班主播列表
|
||||
export function availableZhubo(params) {
|
||||
return request({
|
||||
url: '/admin/liveroom/availableZhubo',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
// 编辑直播间
|
||||
export function saveProducts(data) {
|
||||
return request({
|
||||
url: '/admin/liveroom/saveProducts',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 保存排班信息
|
||||
export function saveRoomWorks(data) {
|
||||
return request({
|
||||
url: '/admin/liveroom/saveRoomWorks',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
<template>
|
||||
<div class="navbar">
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
<hamburger
|
||||
id="hamburger-container"
|
||||
:is-active="sidebar.opened"
|
||||
class="hamburger-container"
|
||||
@toggleClick="toggleSideBar"
|
||||
/>
|
||||
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
|
||||
|
||||
<div class="right-menu">
|
||||
<template v-if="device!=='mobile'">
|
||||
<template v-if="device !== 'mobile'">
|
||||
<!-- <search id="header-search" class="right-menu-item" /> -->
|
||||
|
||||
<!-- <screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||
|
@ -13,23 +18,54 @@
|
|||
<el-tooltip content="Global Size" effect="dark" placement="bottom">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
</el-tooltip> -->
|
||||
|
||||
</template>
|
||||
<!-- <div class="right-menu-item hover-effect">
|
||||
<!-- <div class="right-menu-item hover-effect">
|
||||
<el-button @click="drawer = true">QA常见问题</el-button>
|
||||
</div>-->
|
||||
<div v-if="appointment_num" class="right-menu-item hover-effect">
|
||||
<el-button style="color: red" @click="handleAppointemnet()"
|
||||
>预约待处理({{ appointment_num }})</el-button
|
||||
>
|
||||
</div>
|
||||
<div v-if="$store.getters.is_anchor" class="right-menu-item hover-effect">
|
||||
<el-button @click="dialogWorks = true">排班{{ $store.getters.name }}</el-button>
|
||||
<el-button @click="dialogWorks = true"
|
||||
>排班{{ $store.getters.name }}</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="right-menu-item hover-effect">
|
||||
<el-button :style="{backgroundColor:!workstatus?'#409EFF':'#fff',color:workstatus?'#979797':'#fff'}" @click="startWorks">{{ workstatus?'上班':'上班中' }}</el-button>
|
||||
<el-button :style="{backgroundColor:workstatus?'#409EFF':'#fff',color:workstatus?'#fff':'#979797'}" @click="endWorks">{{ workstatus?'下班中':'下班' }}</el-button>
|
||||
<el-button
|
||||
:style="{
|
||||
backgroundColor: !workstatus ? '#409EFF' : '#fff',
|
||||
color: workstatus ? '#979797' : '#fff',
|
||||
}"
|
||||
@click="startWorks"
|
||||
>{{ workstatus ? "上班" : "上班中" }}</el-button
|
||||
>
|
||||
<el-button
|
||||
:style="{
|
||||
backgroundColor: workstatus ? '#409EFF' : '#fff',
|
||||
color: workstatus ? '#fff' : '#979797',
|
||||
}"
|
||||
@click="endWorks"
|
||||
>{{ workstatus ? "下班中" : "下班" }}</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
|
||||
<el-dropdown
|
||||
class="avatar-container right-menu-item hover-effect"
|
||||
trigger="click"
|
||||
>
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar" alt="">
|
||||
<i class="el-icon-camera" style="position: absolute;" @click.stop="showAvatar = true" />
|
||||
<img
|
||||
:src="avatar + '?imageView2/1/w/80/h/80'"
|
||||
class="user-avatar"
|
||||
alt=""
|
||||
/>
|
||||
<i
|
||||
class="el-icon-camera"
|
||||
style="position: absolute"
|
||||
@click.stop="showAvatar = true"
|
||||
/>
|
||||
<i class="el-icon-caret-bottom" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
|
@ -43,18 +79,12 @@
|
|||
<el-dropdown-item>修改密码</el-dropdown-item>
|
||||
</div>
|
||||
<el-dropdown-item divided @click.native="logout">
|
||||
<span style="display:block;">退出登录</span>
|
||||
<span style="display: block">退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="提示"
|
||||
:visible.sync="showAvatar"
|
||||
width="30%"
|
||||
center
|
||||
>
|
||||
|
||||
<el-dialog title="提示" :visible.sync="showAvatar" width="30%" center>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="/admin/index/avatar"
|
||||
|
@ -62,10 +92,9 @@
|
|||
:on-success="handleAvatarSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
>
|
||||
<img v-if="imageUrl" :src="imageUrl" class="avatar" alt="">
|
||||
<img v-if="imageUrl" :src="imageUrl" class="avatar" alt="" />
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="修改密码" :visible.sync="dialogPWD">
|
||||
|
@ -85,7 +114,7 @@
|
|||
|
||||
<el-dialog title="排班" width="90%" :visible.sync="dialogWorks">
|
||||
<el-form :rules="rules">
|
||||
<el-row style="margin-bottom: 10px;">
|
||||
<el-row style="margin-bottom: 10px">
|
||||
<el-col :span="24">
|
||||
<el-date-picker
|
||||
v-model="times[0]"
|
||||
|
@ -95,7 +124,7 @@
|
|||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="margin-bottom: 10px;">
|
||||
<el-row style="margin-bottom: 10px">
|
||||
<el-col :span="24">
|
||||
<el-date-picker
|
||||
v-model="times[1]"
|
||||
|
@ -105,10 +134,15 @@
|
|||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="margin-bottom: 10px;">
|
||||
<el-row style="margin-bottom: 10px">
|
||||
<el-col :span="24">
|
||||
<el-checkbox-group v-model="os">
|
||||
<el-checkbox v-for="(v,i,k) in $store.getters.oss" :key="k" :label="i">{{ v }}</el-checkbox>
|
||||
<el-checkbox
|
||||
v-for="(v, i, k) in $store.getters.oss"
|
||||
:key="k"
|
||||
:label="i"
|
||||
>{{ v }}</el-checkbox
|
||||
>
|
||||
</el-checkbox-group>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -126,10 +160,21 @@
|
|||
:modal="false"
|
||||
>
|
||||
<div v-clickoutside="handleClose" class="drawer">
|
||||
<el-button v-if="QaShow" type="success" @click="drawer = false">关 闭</el-button>
|
||||
<el-button v-if="!QaShow" type="success" @click="QaShow = true">返 回</el-button>
|
||||
<el-button v-if="QaShow" type="success" @click="drawer = false"
|
||||
>关 闭</el-button
|
||||
>
|
||||
<el-button v-if="!QaShow" type="success" @click="QaShow = true"
|
||||
>返 回</el-button
|
||||
>
|
||||
<div v-if="QaShow" class="mod">
|
||||
<el-button v-for="item in getQaCityList" :key="item.city_id" style="width: 150px;" size="medium" type="primary" @click="clickQaList(item)">
|
||||
<el-button
|
||||
v-for="item in getQaCityList"
|
||||
:key="item.city_id"
|
||||
style="width: 150px"
|
||||
size="medium"
|
||||
type="primary"
|
||||
@click="clickQaList(item)"
|
||||
>
|
||||
{{ item.city_name }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
@ -145,17 +190,17 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
import SizeSelect from '@/components/SizeSelect'
|
||||
import Search from '@/components/HeaderSearch'
|
||||
import { color } from 'echarts/lib/export'
|
||||
import sidebar from '@/layout/components/Sidebar/index.vue'
|
||||
import avatar from 'element-ui/packages/avatar'
|
||||
import {getQaCityList, getQaList} from '@/api/qa'
|
||||
import clickoutside from 'element-ui/src/utils/clickoutside'
|
||||
import { mapGetters } from "vuex";
|
||||
import Breadcrumb from "@/components/Breadcrumb";
|
||||
import Hamburger from "@/components/Hamburger";
|
||||
import Screenfull from "@/components/Screenfull";
|
||||
import SizeSelect from "@/components/SizeSelect";
|
||||
import Search from "@/components/HeaderSearch";
|
||||
import { color } from "echarts/lib/export";
|
||||
import sidebar from "@/layout/components/Sidebar/index.vue";
|
||||
import avatar from "element-ui/packages/avatar";
|
||||
import { getQaCityList, getQaList } from "@/api/qa";
|
||||
import clickoutside from "element-ui/src/utils/clickoutside";
|
||||
export default {
|
||||
directives: { clickoutside },
|
||||
components: {
|
||||
|
@ -164,20 +209,22 @@ export default {
|
|||
Screenfull,
|
||||
SizeSelect,
|
||||
// eslint-disable-next-line vue/no-unused-Wangeditor
|
||||
Search
|
||||
Search,
|
||||
},
|
||||
props: {
|
||||
appointment_num: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
avatar() {
|
||||
return avatar
|
||||
return avatar;
|
||||
},
|
||||
sidebar() {
|
||||
return sidebar
|
||||
return sidebar;
|
||||
},
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
'avatar',
|
||||
'device'
|
||||
])
|
||||
...mapGetters(["sidebar", "avatar", "device"]),
|
||||
},
|
||||
// eslint-disable-next-line vue/order-in-Wangeditor
|
||||
data() {
|
||||
|
@ -195,112 +242,143 @@ export default {
|
|||
getQaLists: [],
|
||||
times: [],
|
||||
form: {
|
||||
oldpwd: '',
|
||||
pwd: ''
|
||||
oldpwd: "",
|
||||
pwd: "",
|
||||
},
|
||||
QaInfo: {
|
||||
title: '',
|
||||
content: ''
|
||||
title: "",
|
||||
content: "",
|
||||
},
|
||||
rules: {
|
||||
oldpwd: [
|
||||
{ required: true, message: '请输入旧密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
{ required: true, message: "请输入旧密码", trigger: "blur" },
|
||||
{
|
||||
min: 6,
|
||||
max: 20,
|
||||
message: "长度在 6 到 20 个字符",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
pwd: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||||
{
|
||||
min: 6,
|
||||
max: 20,
|
||||
message: "长度在 6 到 20 个字符",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getworkstatus()
|
||||
getQaCityList().then(res => {
|
||||
this.getQaCityList = res.data
|
||||
})
|
||||
this.getworkstatus();
|
||||
getQaCityList().then((res) => {
|
||||
this.getQaCityList = res.data;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
color,
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('app/toggleSideBar')
|
||||
this.$store.dispatch("app/toggleSideBar");
|
||||
},
|
||||
handleClose() {
|
||||
this.drawer = false
|
||||
this.QaShow = true
|
||||
this.drawer = false;
|
||||
this.QaShow = true;
|
||||
},
|
||||
clickQaList(data) {
|
||||
getQaList(data.city_id).then(res => {
|
||||
this.getQaLists = res.data
|
||||
})
|
||||
console.log(JSON.stringify(this.getQaLists))
|
||||
getQaList(data.city_id).then((res) => {
|
||||
this.getQaLists = res.data;
|
||||
});
|
||||
console.log(JSON.stringify(this.getQaLists));
|
||||
if (!this.getQaLists) {
|
||||
return this.$message({
|
||||
message: '暂无QA问题',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
})
|
||||
message: "暂无QA问题",
|
||||
type: "warning",
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
this.QaShow = false
|
||||
this.QaShow = false;
|
||||
},
|
||||
async logout() {
|
||||
await this.$store.dispatch('user/logout')
|
||||
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
|
||||
await this.$store.dispatch("user/logout");
|
||||
this.$router.push(`/login?redirect=${this.$route.fullPath}`);
|
||||
},
|
||||
handleAvatarSuccess(res, file) {
|
||||
this.imageUrl = URL.createObjectURL(file.raw)
|
||||
this.imageUrl = URL.createObjectURL(file.raw);
|
||||
},
|
||||
pwd() {
|
||||
this.$axios.post('/admin/admin/pwd', this.form).then(res => {
|
||||
this.dialogPWD = false
|
||||
this.form = {}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
this.$axios
|
||||
.post("/admin/admin/pwd", this.form)
|
||||
.then((res) => {
|
||||
this.dialogPWD = false;
|
||||
this.form = {};
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
saveWork() {
|
||||
this.$axios.post('/admin/work/save2', { times: this.times, os: this.os }).then(res => {
|
||||
console.log(res)
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '添加成功'
|
||||
this.$axios
|
||||
.post("/admin/work/save2", { times: this.times, os: this.os })
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: "添加成功",
|
||||
});
|
||||
this.dialogWorks = false;
|
||||
})
|
||||
this.dialogWorks = false
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
startWorks() {
|
||||
this.$axios.post('/admin/admin/editInfo', { is_order: 1 }).then(res => {
|
||||
console.log(res)
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '上班成功'
|
||||
this.$axios
|
||||
.post("/admin/admin/editInfo", { is_order: 1 })
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: "上班成功",
|
||||
});
|
||||
this.getworkstatus();
|
||||
})
|
||||
this.getworkstatus()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
endWorks() {
|
||||
this.$axios.post('/admin/admin/editInfo', { is_order: 0 }).then(res => {
|
||||
console.log(res)
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '下班成功'
|
||||
this.$axios
|
||||
.post("/admin/admin/editInfo", { is_order: 0 })
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: "下班成功",
|
||||
});
|
||||
this.getworkstatus();
|
||||
})
|
||||
this.getworkstatus()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
getworkstatus() {
|
||||
this.$axios.post('/admin/work/getworkstatus', { id: this.id }).then(res => {
|
||||
console.log(res)
|
||||
this.workstatus = res.data
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
this.$axios
|
||||
.post("/admin/work/getworkstatus", { id: this.id })
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
this.workstatus = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
handleAppointemnet() {
|
||||
this.$router.push({
|
||||
path: "/order/index/",
|
||||
query: { appointment_status: "1", refresh: Date.now() },
|
||||
});
|
||||
},
|
||||
beforeAvatarUpload(file) {
|
||||
/*const isJPG = file.type === 'image/jpeg'
|
||||
|
@ -313,29 +391,29 @@ export default {
|
|||
this.$message.error('上传头像图片大小不能超过 2MB!')
|
||||
}
|
||||
return isJPG && isLt2M*/
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer{
|
||||
.drawer {
|
||||
padding: 20px 0 0 20px;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
.mod{
|
||||
.mod {
|
||||
margin-top: 50px;
|
||||
}
|
||||
.ver{
|
||||
.ver {
|
||||
margin-top: 30px;
|
||||
color: #6c6f71;
|
||||
&_title{
|
||||
&_title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
&_content{
|
||||
&_content {
|
||||
line-height: 26px;
|
||||
font-size: 15px;
|
||||
padding-bottom: 10px;
|
||||
|
@ -348,18 +426,18 @@ export default {
|
|||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
transition: background .3s;
|
||||
-webkit-tap-highlight-color:transparent;
|
||||
transition: background 0.3s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, .025)
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,10 +469,10 @@ export default {
|
|||
|
||||
&.hover-effect {
|
||||
cursor: pointer;
|
||||
transition: background .3s;
|
||||
transition: background 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, .025)
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -433,7 +511,7 @@ export default {
|
|||
overflow: hidden;
|
||||
}
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: #409EFF;
|
||||
border-color: #409eff;
|
||||
}
|
||||
.avatar-uploader-icon {
|
||||
border: 1px solid #979797;
|
||||
|
|
|
@ -1,16 +1,37 @@
|
|||
<template>
|
||||
<div v-if="!item.hidden">
|
||||
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
|
||||
<template
|
||||
v-if="
|
||||
hasOneShowingChild(item.children, item) &&
|
||||
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
|
||||
!item.alwaysShow
|
||||
"
|
||||
>
|
||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
|
||||
<el-menu-item
|
||||
:index="resolvePath(onlyOneChild.path)"
|
||||
:class="{ 'submenu-title-noDropdown': !isNest }"
|
||||
>
|
||||
<item
|
||||
:icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"
|
||||
:title="onlyOneChild.meta.title"
|
||||
/>
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
|
||||
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
|
||||
<el-submenu
|
||||
v-else
|
||||
ref="subMenu"
|
||||
:index="resolvePath(item.path)"
|
||||
popper-append-to-body
|
||||
>
|
||||
<template slot="title">
|
||||
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
|
||||
<item
|
||||
v-if="item.meta"
|
||||
:icon="item.meta && item.meta.icon"
|
||||
:title="item.meta.title"
|
||||
/>
|
||||
</template>
|
||||
<sidebar-item
|
||||
v-for="child in item.children"
|
||||
|
@ -25,71 +46,72 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import Item from './Item'
|
||||
import AppLink from './Link'
|
||||
import FixiOSBug from './FixiOSBug'
|
||||
import path from "path";
|
||||
import { isExternal } from "@/utils/validate";
|
||||
import Item from "./Item";
|
||||
import AppLink from "./Link";
|
||||
import FixiOSBug from "./FixiOSBug";
|
||||
|
||||
export default {
|
||||
name: 'SidebarItem',
|
||||
name: "SidebarItem",
|
||||
components: { Item, AppLink },
|
||||
mixins: [FixiOSBug],
|
||||
props: {
|
||||
// route object
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
// To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
|
||||
// TODO: refactor with render function
|
||||
this.onlyOneChild = null
|
||||
return {}
|
||||
this.onlyOneChild = null;
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
hasOneShowingChild(children = [], parent) {
|
||||
const showingChildren = children.filter(item => {
|
||||
const showingChildren = children.filter((item) => {
|
||||
if (item.hidden) {
|
||||
return false
|
||||
return false;
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
this.onlyOneChild = item
|
||||
return true
|
||||
this.onlyOneChild = item;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show parent if there are no child router to display
|
||||
if (showingChildren.length === 0) {
|
||||
this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
|
||||
return true
|
||||
this.onlyOneChild = { ...parent, path: "", noShowingChildren: true };
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
resolvePath(routePath) {
|
||||
routePath = routePath.replace(/\/:[a-zA-Z]+/g, "");
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
return routePath;
|
||||
}
|
||||
if (isExternal(this.basePath)) {
|
||||
return this.basePath
|
||||
return this.basePath;
|
||||
}
|
||||
return path.resolve(this.basePath, routePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
return path.resolve(this.basePath, routePath);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<template>
|
||||
<div :class="classObj" class="app-wrapper">
|
||||
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<div
|
||||
v-if="device === 'mobile' && sidebar.opened"
|
||||
class="drawer-bg"
|
||||
@click="handleClickOutside"
|
||||
/>
|
||||
<sidebar class="sidebar-container" />
|
||||
<div :class="{hasTagsView:needTagsView}" class="main-container">
|
||||
<div :class="{'fixed-header':fixedHeader}">
|
||||
<navbar />
|
||||
<div :class="{ hasTagsView: needTagsView }" class="main-container">
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<navbar :appointment_num="appointment_num" />
|
||||
<tags-view v-if="needTagsView" />
|
||||
</div>
|
||||
<app-main />
|
||||
|
@ -16,127 +20,193 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import RightPanel from '@/components/RightPanel'
|
||||
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
|
||||
import ResizeMixin from './mixin/ResizeHandler'
|
||||
import { mapState } from 'vuex'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import RightPanel from "@/components/RightPanel";
|
||||
import { AppMain, Navbar, Settings, Sidebar, TagsView } from "./components";
|
||||
import ResizeMixin from "./mixin/ResizeHandler";
|
||||
import { mapState } from "vuex";
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: 'Layout',
|
||||
name: "Layout",
|
||||
components: {
|
||||
AppMain,
|
||||
Navbar,
|
||||
RightPanel,
|
||||
Settings,
|
||||
Sidebar,
|
||||
TagsView
|
||||
TagsView,
|
||||
},
|
||||
mixins: [ResizeMixin],
|
||||
computed: {
|
||||
...mapState({
|
||||
sidebar: state => state.app.sidebar,
|
||||
device: state => state.app.device,
|
||||
showSettings: state => state.settings.showSettings,
|
||||
needTagsView: state => state.settings.tagsView,
|
||||
fixedHeader: state => state.settings.fixedHeader
|
||||
sidebar: (state) => state.app.sidebar,
|
||||
device: (state) => state.app.device,
|
||||
showSettings: (state) => state.settings.showSettings,
|
||||
needTagsView: (state) => state.settings.tagsView,
|
||||
fixedHeader: (state) => state.settings.fixedHeader,
|
||||
}),
|
||||
classObj() {
|
||||
return {
|
||||
hideSidebar: !this.sidebar.opened,
|
||||
openSidebar: this.sidebar.opened,
|
||||
withoutAnimation: this.sidebar.withoutAnimation,
|
||||
mobile: this.device === 'mobile'
|
||||
}
|
||||
}
|
||||
mobile: this.device === "mobile",
|
||||
};
|
||||
},
|
||||
},
|
||||
data(){
|
||||
data() {
|
||||
return {
|
||||
iswork:null
|
||||
}
|
||||
iswork: null,
|
||||
appointment_num: 0,
|
||||
};
|
||||
},
|
||||
created(){
|
||||
this.iswork = setInterval(()=>{
|
||||
let toke = getToken()
|
||||
if(!toke || toke.length <= 0) return;
|
||||
this.$axios.get('/admin/index/iswork').then(res=>{
|
||||
if(res && res.data.new > 0) {
|
||||
this.$notify({
|
||||
title: '新的订单提醒',
|
||||
duration: 0,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: '<strong>你有('+res.data.new+')个新的订单,需要处理</strong>'
|
||||
});
|
||||
}
|
||||
if(res && res.data.follow > 0) {
|
||||
this.$notify({
|
||||
title: '新的跟进提醒',
|
||||
duration: 10000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: '<strong>你有('+res.data.follow+')个跟进订单,需要处理</strong>'
|
||||
});
|
||||
}
|
||||
if(res && res.data.back > 0) {
|
||||
this.$notify({
|
||||
title: '转单申请',
|
||||
duration: 10000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: '<strong>你有('+res.data.back+')个转单订单,需要处理</strong>'
|
||||
});
|
||||
}
|
||||
}).catch(err=>{
|
||||
console.log(err)
|
||||
})
|
||||
},30000);
|
||||
created() {
|
||||
this.iswork = setInterval(() => {
|
||||
let toke = getToken();
|
||||
if (!toke || toke.length <= 0) return;
|
||||
this.$axios
|
||||
.get("/admin/index/iswork")
|
||||
.then((res) => {
|
||||
// console.log(res);
|
||||
|
||||
this.appointment_num = res.data.appointment_num;
|
||||
console.log(this.appointment_num);
|
||||
|
||||
if (res && res.data.new > 0) {
|
||||
this.$notify({
|
||||
title: "新的订单提醒",
|
||||
duration: 0,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message:
|
||||
"<strong>你有(" +
|
||||
res.data.new +
|
||||
")个新的订单,需要处理</strong>",
|
||||
});
|
||||
}
|
||||
if (res && res.data.follow > 0) {
|
||||
this.$notify({
|
||||
title: "新的跟进提醒",
|
||||
duration: 10000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message:
|
||||
"<strong>你有(" +
|
||||
res.data.follow +
|
||||
")个跟进订单,需要处理</strong>",
|
||||
});
|
||||
}
|
||||
if (res && res.data.back > 0) {
|
||||
this.$notify({
|
||||
title: "转单申请",
|
||||
duration: 10000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message:
|
||||
"<strong>你有(" +
|
||||
res.data.back +
|
||||
")个转单订单,需要处理</strong>",
|
||||
});
|
||||
}
|
||||
if (res && res.data.order_write_off > 0) {
|
||||
this.$notify({
|
||||
title: "订单核销",
|
||||
duration: 10000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message:
|
||||
"<strong>你有(" +
|
||||
res.data.order_write_off +
|
||||
")个核销订单</strong>",
|
||||
});
|
||||
}
|
||||
if (res && res.data.follow_message > 0) {
|
||||
this.$notify({
|
||||
// res.data.follow_order_id
|
||||
title: "跟进提醒",
|
||||
duration: 50000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message:
|
||||
"<strong>你有(" +
|
||||
res.data.follow_message +
|
||||
")个跟进提醒</strong>",
|
||||
onClick: () => {
|
||||
// console.log(res.data.follow_order_id);
|
||||
this.$router.push({
|
||||
path: "/order/index/",
|
||||
query: { id: res.data.follow_order_id, refresh: Date.now() },
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}, 30000);
|
||||
},
|
||||
methods: {
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
},
|
||||
followDetail(orderId) {
|
||||
console.log(orderId);
|
||||
// this.$router.push({ path: "/order/index/" + orderId });
|
||||
},
|
||||
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) => {});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/styles/mixin.scss";
|
||||
@import "~@/styles/variables.scss";
|
||||
@import "~@/styles/mixin.scss";
|
||||
@import "~@/styles/variables.scss";
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$sideBarWidth});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px)
|
||||
}
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$sideBarWidth});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px);
|
||||
}
|
||||
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,7 +3,7 @@ import Vue from 'vue'
|
|||
import Cookies from 'js-cookie'
|
||||
|
||||
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
|
||||
|
||||
// import consts from '@/utils/consts'
|
||||
import Element from 'element-ui'
|
||||
import './styles/element-variables.scss'
|
||||
// import enLang from 'element-ui/lib/locale/lang/en'// 如果使用中文语言包请默认支持,无需额外引入,请删除该依赖
|
||||
|
@ -36,7 +36,7 @@ if (process.env.NODE_ENV === 'production') {
|
|||
const { mockXHR } = require('../mock')
|
||||
mockXHR()
|
||||
}
|
||||
|
||||
// Vue.prototype.$consts = consts
|
||||
Vue.use(Element, {
|
||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
||||
// locale: enLang // 如果使用中文,无需设置,请删除
|
||||
|
|
|
@ -103,6 +103,15 @@ export const asyncRoutes = [
|
|||
roles: ['admin']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'scheduling',
|
||||
component: () => import('@/views/scheduling/index'),
|
||||
name: 'scheduling',
|
||||
meta: {
|
||||
title: '直播排班',
|
||||
roles: ['admin']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'onlines',
|
||||
component: () => import('@/views/onlines/online.vue'),
|
||||
|
@ -189,6 +198,24 @@ export const asyncRoutes = [
|
|||
roles: ['order_back', 'editor']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'refunded',
|
||||
component: () => import('@/views/order/refunded'),
|
||||
name: 'OrderBack',
|
||||
meta: {
|
||||
title: '已退款订单',
|
||||
roles: ['order_back', 'editor']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'appointment',
|
||||
component: () => import('@/views/order/appointment'),
|
||||
name: 'appointment',
|
||||
meta: {
|
||||
title: '预约记录',
|
||||
roles: ['order_back', 'editor']
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import axios from 'axios'
|
||||
import { MessageBox, Message } from 'element-ui'
|
||||
import store from '@/store'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { getToken,removeToken } from '@/utils/auth'
|
||||
|
||||
// create an axios instance
|
||||
const service = axios.create({
|
||||
|
@ -65,6 +65,9 @@ service.interceptors.response.use(
|
|||
})
|
||||
})
|
||||
}
|
||||
if (res.code == 400) {
|
||||
removeToken()
|
||||
}
|
||||
return Promise.reject(new Error(res.message || 'Error'))
|
||||
} else {
|
||||
return res
|
||||
|
|
|
@ -1,93 +1,197 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.username" placeholder="用户名" style="width: 200px; margin-right: 10px;" class="filter-item" />
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="getList">
|
||||
<el-input
|
||||
v-model="listQuery.username"
|
||||
placeholder="用户名"
|
||||
style="width: 200px; margin-right: 10px"
|
||||
class="filter-item"
|
||||
/>
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="getList"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-circle-plus" @click="handleCreate({routes:[]})">
|
||||
<el-button
|
||||
class="filter-item"
|
||||
style="margin-left: 10px"
|
||||
type="primary"
|
||||
icon="el-icon-circle-plus"
|
||||
@click="handleCreate({ routes: [] })"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
|
||||
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column align="center" label="操作" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" @click="handleCreate(scope.row)" size="small" icon="el-icon-edit">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleCreate(scope.row)"
|
||||
size="small"
|
||||
icon="el-icon-edit"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" v-if="scope.row.status" @click="onWork(scope.row)" size="small" icon="el-icon-date">
|
||||
<el-button
|
||||
type="primary"
|
||||
v-if="scope.row.status"
|
||||
@click="onWork(scope.row)"
|
||||
size="small"
|
||||
icon="el-icon-date"
|
||||
>
|
||||
排班
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="ID" width="80" prop="id"></el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="ID"
|
||||
width="80"
|
||||
prop="id"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column align="center" label="用户名" width="160" prop="username"></el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="用户名"
|
||||
width="160"
|
||||
prop="username"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column align="center" label="姓名" width="160" prop="name"></el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="姓名"
|
||||
width="160"
|
||||
prop="name"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column align="center" label="手机" width="160" prop="mobile"></el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="手机"
|
||||
width="160"
|
||||
prop="mobile"
|
||||
></el-table-column>
|
||||
<!-- <el-table-column width="138px" align="center" label="路线">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.route_type === 10" type="border-card"
|
||||
>境内路线</el-tag
|
||||
>
|
||||
<el-tag v-if="scope.row.route_type === 20" type="success"
|
||||
>境外路线</el-tag
|
||||
>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
|
||||
<!-- <el-table-column align="center" label="头像">
|
||||
<!-- <el-table-column align="center" label="头像">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :size="50" :src="scope.row.avatar"></el-avatar>
|
||||
</template>
|
||||
</el-table-column>-->
|
||||
|
||||
<el-table-column class-name="status-col" label="状态" width="180">
|
||||
<template slot-scope="{row}">
|
||||
<template slot-scope="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
@change="setStatus(row)"
|
||||
active-text="启用"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
inactive-text="禁用">
|
||||
inactive-text="禁用"
|
||||
>
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="180px" align="center" label="创建时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.create_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
<span>{{
|
||||
scope.row.create_time | parseTime("{y}-{m}-{d} {h}:{i}")
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="180px" align="center" label="修改时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.update_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
<span>{{
|
||||
scope.row.update_time | parseTime("{y}-{m}-{d} {h}:{i}")
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
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="120px" :model="item">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="item.username" name="username" placeholder="管理员的用户名"></el-input>
|
||||
<el-input
|
||||
v-model="item.username"
|
||||
name="username"
|
||||
placeholder="管理员的用户名"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="item.name" name="name" placeholder="管理员的姓名"></el-input>
|
||||
<el-input
|
||||
v-model="item.name"
|
||||
name="name"
|
||||
placeholder="管理员的姓名"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机">
|
||||
<el-input v-model="item.mobile" name="name" placeholder="发送短时的时候会用到这个手机"></el-input>
|
||||
<el-input
|
||||
v-model="item.mobile"
|
||||
name="name"
|
||||
placeholder="发送短时的时候会用到这个手机"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信">
|
||||
<el-input
|
||||
v-model="item.wechat"
|
||||
name="wechat"
|
||||
placeholder="微信号"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信图片">
|
||||
<el-upload
|
||||
action = ""
|
||||
list-type="picture-card"
|
||||
multiple:false
|
||||
:show-file-list="false"
|
||||
:http-request="handlesAvatarSuccess"
|
||||
:on-success="
|
||||
(response, file, fileList) =>
|
||||
handleSuccess(response, file, fileList, 1)"
|
||||
>
|
||||
<img v-if="item.wechat_pic" :src="item.wechat_pic" style="width: 120px; height: 120px;margin-top: 14px;"/>
|
||||
<i slot="default" class="el-icon-plus"></i>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="item.password" type="password" name="password" placeholder="管理员的新密码"></el-input>
|
||||
<el-input
|
||||
v-model="item.password"
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="管理员的新密码"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否分配">
|
||||
<el-switch
|
||||
|
@ -96,7 +200,7 @@
|
|||
:active-value="1"
|
||||
inactive-text="不分配"
|
||||
:inactive-value="0"
|
||||
>
|
||||
>
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否主播">
|
||||
|
@ -106,7 +210,7 @@
|
|||
:active-value="1"
|
||||
inactive-text=""
|
||||
:inactive-value="0"
|
||||
>
|
||||
>
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否加盟商">
|
||||
|
@ -116,23 +220,42 @@
|
|||
:active-value="1"
|
||||
inactive-text=""
|
||||
:inactive-value="0"
|
||||
>
|
||||
>
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="产品ID">
|
||||
<!-- <el-form-item label="产品ID">
|
||||
<el-input v-model="item.product_ids" type="textarea" />
|
||||
<span style="color: red;font-size: 11px;">多个用英文逗号隔开,例如: 384731,2328</span>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="路线">
|
||||
<el-checkbox-group v-model="item.routes">
|
||||
<el-checkbox label="复选框 A">111</el-checkbox>
|
||||
<el-checkbox label="复选框 B">2222</el-checkbox>
|
||||
<el-checkbox label="复选框 C">3333</el-checkbox>
|
||||
<el-checkbox label="禁用" disabled></el-checkbox>
|
||||
<el-checkbox label="选中且禁用" disabled></el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<span style="color: red; font-size: 11px"
|
||||
>多个用英文逗号隔开,例如: 384731,2328</span
|
||||
>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="线路">
|
||||
<el-select
|
||||
filterable
|
||||
v-model="item.product_ids"
|
||||
multiple
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.id"
|
||||
:label="item.product_name"
|
||||
:value="item.id"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div class="addRoutes">
|
||||
<el-button
|
||||
class="filter-item"
|
||||
style="margin-left: 10px"
|
||||
type="primary"
|
||||
icon="el-icon-circle-plus"
|
||||
@click="handleAddRoutes"
|
||||
>添加线路</el-button
|
||||
>
|
||||
</div>
|
||||
<!-- <el-form-item label="路线">
|
||||
<el-select multiple v-model="item.routes" filterable placeholder="请选择">
|
||||
<el-option
|
||||
|
@ -143,12 +266,12 @@
|
|||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="路线">
|
||||
<!-- <el-form-item label="路线">
|
||||
<el-radio-group v-model="item.route_type">
|
||||
<el-radio :label="10">境内跟团</el-radio>
|
||||
<el-radio :label="20">境外跟团</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="onSave(item)">保 存</el-button>
|
||||
|
@ -157,13 +280,13 @@
|
|||
|
||||
<el-dialog title="添加排班" :visible.sync="dialogWork">
|
||||
<el-form label-width="120px" :model="from">
|
||||
|
||||
<el-form-item label="上班日期">
|
||||
<el-date-picker
|
||||
style="margin-right: 10px;"
|
||||
style="margin-right: 10px"
|
||||
type="dates"
|
||||
v-model="from.dates"
|
||||
placeholder="选择一个或多个日期">
|
||||
placeholder="选择一个或多个日期"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
|
@ -180,127 +303,272 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item label="渠道">
|
||||
<el-checkbox-group v-model="from.oss" >
|
||||
<el-checkbox v-for="(v,i,k) in oss" :label="i" :key="k">{{v}}</el-checkbox>
|
||||
<el-checkbox-group v-model="from.oss">
|
||||
<el-checkbox v-for="(v, i, k) in oss" :label="i" :key="k">{{
|
||||
v
|
||||
}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="onWork()">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="添加线路" :visible.sync="isAddRouters" width="30%">
|
||||
<el-form
|
||||
:rules="addRoutes"
|
||||
ref="AddRoutersForm"
|
||||
:model="AddRoutersForm"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item label="平台" prop="os">
|
||||
<el-select v-model="AddRoutersForm.os" placeholder="请选择平台">
|
||||
<el-option
|
||||
v-for="item in platformList"
|
||||
:label="item.os"
|
||||
:value="item.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="线路名称" prop="product_name">
|
||||
<el-input
|
||||
placeholder="请输入线路名称"
|
||||
v-model="AddRoutersForm.product_name"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="线路id" prop="third_product_id">
|
||||
<el-input
|
||||
placeholder="请输入线路id"
|
||||
v-model="AddRoutersForm.third_product_id"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="isAddRouters = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleAddRouters">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
|
||||
|
||||
import Pagination from "@/components/Pagination"; // Secondary package based on el-pagination
|
||||
import { getProductsList, addProducts } from "@/api/admin";
|
||||
import { getToken } from "@/utils/auth";
|
||||
export default {
|
||||
name: 'Adminlist',
|
||||
name: "Adminlist",
|
||||
components: { Pagination },
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
ids: "",
|
||||
oss: [],
|
||||
from: {oss:[]},
|
||||
platformList: [],
|
||||
isAddRouters: false,
|
||||
AddRoutersForm: {
|
||||
third_product_id: "",
|
||||
product_name: "",
|
||||
os: "",
|
||||
},
|
||||
from: { oss: [] },
|
||||
total: 0,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20
|
||||
limit: 20,
|
||||
},
|
||||
dialogVisible: false,
|
||||
dialogWork: false,
|
||||
item: {
|
||||
btn:[]
|
||||
btn: [],
|
||||
product_ids: [],
|
||||
},
|
||||
route_type: '',
|
||||
// options: [{
|
||||
// value: '1',
|
||||
// label: '路线1'
|
||||
// }, {
|
||||
// value: '2',
|
||||
// label: '路线2'
|
||||
// }, {
|
||||
// value: '3',
|
||||
// label: '路线3'
|
||||
// }, {
|
||||
// value: '4',
|
||||
// label: '路线4'
|
||||
// }, {
|
||||
// value: '5',
|
||||
// label: '路线5'
|
||||
// }],
|
||||
}
|
||||
route_type: "",
|
||||
options: [],
|
||||
addRoutes: {
|
||||
third_product_id: [
|
||||
{ required: true, message: "请输入线路id", trigger: "blur" },
|
||||
],
|
||||
os: [{ required: true, message: "请选择平台", trigger: "change" }],
|
||||
product_name: [
|
||||
{ required: true, message: "请输入线路名称", trigger: "blur" },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.$axios.get('/admin/admin/index', { params: this.listQuery }).then(response => {
|
||||
this.list = response.data.data
|
||||
this.total = response.data.total
|
||||
this.oss = response.ext.oss
|
||||
// for(let k in response.ext.oss){
|
||||
// this.oss.push({id:k,val:response.ext.oss[k]})
|
||||
// }
|
||||
console.log(this.oss)
|
||||
this.listLoading = false
|
||||
}).catch(err=>{
|
||||
})
|
||||
this.$axios
|
||||
.get("/admin/admin/index", { params: this.listQuery })
|
||||
.then((response) => {
|
||||
this.list = response.data.data;
|
||||
this.total = response.data.total;
|
||||
this.oss = response.ext.oss;
|
||||
// for(let k in response.ext.oss){
|
||||
// this.oss.push({id:k,val:response.ext.oss[k]})
|
||||
// }
|
||||
console.log(this.oss);
|
||||
this.listLoading = false;
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
checkIfUrlContainsImage(url = "") {
|
||||
const imageExtensions = [
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".png",
|
||||
".gif",
|
||||
".bmp",
|
||||
".svg",
|
||||
".webp",
|
||||
];
|
||||
return imageExtensions.some((extension) =>
|
||||
url.toLowerCase().endsWith(extension)
|
||||
);
|
||||
},
|
||||
handleAddRoutes() {
|
||||
this.isAddRouters = true;
|
||||
},
|
||||
async handlesAvatarSuccess(file) {
|
||||
try {
|
||||
var formdata = new FormData();
|
||||
formdata.append("file", file.file);
|
||||
|
||||
this.upLoading = true;
|
||||
const _this = this;
|
||||
const res = await this.$axios.post("/admin/upload/index", formdata, {
|
||||
headers: {
|
||||
"Content-type": "multipart/form-data",
|
||||
"X-Token": getToken(),
|
||||
},
|
||||
});
|
||||
this.item.wechat_pic = `${window.location.protocol}//${window.location.host}${res.data}`;
|
||||
file.onSuccess(res);
|
||||
} catch (error) {
|
||||
console.error('error:', error);
|
||||
}
|
||||
},
|
||||
handleSuccess(res, file, fileList) {
|
||||
console.log(res, file, fileList);
|
||||
if (!res.data) return;
|
||||
this.item.wechat_pic = `${window.location.protocol}//${window.location.host}${res.data}`;
|
||||
},
|
||||
handleAddRouters() {
|
||||
this.$refs["AddRoutersForm"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
let add = await addProducts(this.AddRoutersForm);
|
||||
this.isAddRouters = false;
|
||||
if (add.msg == "ok") {
|
||||
this.$message({
|
||||
message: "线路添加成功",
|
||||
type: "success",
|
||||
});
|
||||
let res = await getProductsList();
|
||||
this.options = res.data.data;
|
||||
this.platformList = res.ext.oss;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
setStatus(item) {
|
||||
console.log(item)
|
||||
this.$axios.post('/admin/admin/disabled',item).then(res=>{
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}).catch(err=>{
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改失败',
|
||||
type: 'error',
|
||||
duration: 2000
|
||||
console.log(item);
|
||||
this.$axios
|
||||
.post("/admin/admin/disabled", item)
|
||||
.then((res) => {
|
||||
this.$notify({
|
||||
title: "成功",
|
||||
message: "修改成功",
|
||||
type: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$notify({
|
||||
title: "成功",
|
||||
message: "修改失败",
|
||||
type: "error",
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
},
|
||||
handleCreate(item) {
|
||||
async handleCreate(item) {
|
||||
this.dialogVisible = true;
|
||||
this.item = item
|
||||
let res = await getProductsList();
|
||||
// console.log(res.data.data);
|
||||
this.options = res.data.data;
|
||||
this.platformList = res.ext.oss;
|
||||
this.item = { ...item };
|
||||
this.item.product_ids = item.product_ids.split(",").map((item) => {
|
||||
return Number(item);
|
||||
});
|
||||
},
|
||||
onSave(from) {
|
||||
this.$axios.post('/admin/admin/save', from).then(response => {
|
||||
this.item = {}
|
||||
this.dialogVisible = false
|
||||
this.getList()
|
||||
}).catch(err=>{
|
||||
})
|
||||
this.$axios
|
||||
.post("/admin/admin/save", {
|
||||
...from,
|
||||
product_ids: from.product_ids.join(),
|
||||
})
|
||||
.then((response) => {
|
||||
this.$message({
|
||||
message: "成功",
|
||||
type: "success",
|
||||
});
|
||||
this.item = {};
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
onWork(item) {
|
||||
if(item) {
|
||||
this.item = item
|
||||
this.from = {oss:[]}
|
||||
this.dialogWork = true
|
||||
return
|
||||
if (item) {
|
||||
this.item = item;
|
||||
this.from = { oss: [] };
|
||||
this.dialogWork = true;
|
||||
return;
|
||||
}
|
||||
this.from.admin_id = this.item.id;
|
||||
this.$axios.post('/admin/work/saves', this.from).then(response => {
|
||||
this.item = {}
|
||||
this.dialogWork = false
|
||||
}).catch(err=>{
|
||||
|
||||
})
|
||||
this.$axios
|
||||
.post("/admin/work/saves", this.from)
|
||||
.then((response) => {
|
||||
this.item = {};
|
||||
this.dialogWork = false;
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
setTime(val) {
|
||||
console.log(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(val);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep.el-select {
|
||||
width: 100%;
|
||||
}
|
||||
.addRoutes {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
// .el-input__suffix .el-input__suffix-inner {
|
||||
// // 解决聚焦的时候会有闪现一下滚动条
|
||||
// .el-input__icon {
|
||||
// transform: rotateZ(0deg);
|
||||
// // transition: transform 20s;
|
||||
// // background: pink;
|
||||
// &::before {
|
||||
// display: inline-block;
|
||||
// transition: transform 0.3s;
|
||||
// transform: rotateZ(180deg);
|
||||
// }
|
||||
// &.is-reverse::before {
|
||||
// transform: rotateZ(0deg);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
|
|
@ -1,226 +1,309 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.username" placeholder="用户名" style="width: 200px; margin-right: 10px;"
|
||||
class="filter-item" />
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="getList">
|
||||
<el-input
|
||||
v-model="listQuery.mobile"
|
||||
placeholder="手机号"
|
||||
style="width: 200px; margin-right: 10px"
|
||||
class="filter-item"
|
||||
/>
|
||||
<el-date-picker
|
||||
v-model="listQuery.times"
|
||||
class="filter-item"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
/>
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="getList"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-circle-plus" @click="onAdd">
|
||||
添加主播排班
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-circle-plus"
|
||||
@click="onAdd"
|
||||
>
|
||||
添加主播
|
||||
</el-button>
|
||||
|
||||
</div>
|
||||
|
||||
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
|
||||
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column align="center" fixed label="操作" width="220">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" @click="onDel(scope.row)" size="small" icon="el-icon-delete-solid">
|
||||
<!-- <el-button
|
||||
type="danger"
|
||||
@click="onDel(scope.row)"
|
||||
size="small"
|
||||
icon="el-icon-delete-solid"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</el-button> -->
|
||||
|
||||
<el-button type="primary" @click="onSave(scope.row)" size="small" icon="el-icon-edit">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="onAdd(scope.row)"
|
||||
size="small"
|
||||
icon="el-icon-edit"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="ID" width="80">
|
||||
<!-- <el-table-column align="center" label="ID" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
|
||||
<el-table-column align="center" label="用户名" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.admin.username }}</span>
|
||||
<span>{{ scope.row.username }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="姓名" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.admin.name }}</span>
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column align="center" width="100px" label="头像">
|
||||
<el-table-column align="center" label="手机号" width="160">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :size="50" :src="scope.row.admin.avatar"></el-avatar>
|
||||
</template>
|
||||
</el-table-column>-->
|
||||
|
||||
<el-table-column align="center" label="排班时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.start| parseTime('{y}-{m}-{d} {h}:{i}') }} - {{ scope.row.end| parseTime('{y}-{m}-{d} {h}:{i}') }}
|
||||
<span>{{ scope.row.mobile }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="岗位" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.type == 2 ? "主播" : "中控" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column align="center" label="排班时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.start | parseTime("{y}-{m}-{d} {h}:{i}") }} -
|
||||
{{ scope.row.end | parseTime("{y}-{m}-{d} {h}:{i}") }}
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
|
||||
<el-table-column align="center" label="平台">
|
||||
<!-- <el-table-column align="center" label="平台">
|
||||
<template slot-scope="scope">
|
||||
<template v-for="i in scope.row.os">
|
||||
{{ oss[i] }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
|
||||
<el-table-column align="center" label="订单数">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.orders }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="直播时长(小时)">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.work_time }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="订单金额">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.total/100 }}
|
||||
{{ scope.row.total }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="140px" align="center" label="创建时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.create_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
<span>{{
|
||||
scope.row.create_time | parseTime("{y}-{m}-{d} {h}:{i}")
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="140px" align="center" label="修改时间">
|
||||
<!-- <el-table-column width="140px" align="center" label="修改时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.update_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
<span>{{
|
||||
scope.row.update_time | parseTime("{y}-{m}-{d} {h}:{i}")
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page.sync="listQuery.page"
|
||||
:limit.sync="listQuery.limit"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<!--
|
||||
<el-dialog title="修改排班" :visible.sync="dialogWork">
|
||||
<el-form label-width="120px" :model="item">
|
||||
|
||||
<el-form-item label="上班时间">
|
||||
<el-date-picker
|
||||
v-model="item.date"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期">
|
||||
end-placeholder="结束日期"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="渠道">
|
||||
<el-checkbox-group v-model="item.oss">
|
||||
<el-checkbox v-for="(v,i,k) in oss" :label="i" :key="k">{{v}}</el-checkbox>
|
||||
<el-checkbox v-for="(v, i, k) in oss" :label="i" :key="k">{{
|
||||
v
|
||||
}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="onSave()">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-dialog> -->
|
||||
|
||||
<el-dialog title="主播排班表" :visible.sync="dialogCreate">
|
||||
<el-form label-width="120px">
|
||||
<el-row v-for="an in anchors" :key="an.id" style="margin-bottom: 10px;">
|
||||
<el-col :span="2">{{ an.name }}</el-col>
|
||||
<el-col :span="14">
|
||||
<el-date-picker
|
||||
v-model="an.times"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
:default-time="['07:00:00', '23:59:59']"
|
||||
style="width:90%"
|
||||
end-placeholder="结束日期">
|
||||
</el-date-picker>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-checkbox-group v-model="an.os">
|
||||
<el-checkbox v-for="(v,i,k) in oss" :label="i" :key="k">{{v}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog :title="title" :visible.sync="dialogCreate">
|
||||
<el-form ref="form" :model="form" label-width="80px">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input placeholder="用户名" v-model="form.username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input placeholder="姓名" v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input placeholder="请输入手机号" v-model="form.mobile"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位" prop="type">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio :label="2">主播</el-radio>
|
||||
<el-radio :label="3">中控</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" v-loading="loading" @click="onSaves()">保 存</el-button>
|
||||
<el-button type="primary" v-loading="loading" @click="onSaves(form)"
|
||||
>保 存</el-button
|
||||
>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
|
||||
import Pagination from "@/components/Pagination"; // Secondary package based on el-pagination
|
||||
|
||||
export default {
|
||||
name: 'Works',
|
||||
name: "Works",
|
||||
components: { Pagination },
|
||||
data() {
|
||||
return {
|
||||
oss: {},
|
||||
list: null,
|
||||
title: "",
|
||||
total: 0,
|
||||
listLoading: true,
|
||||
loading: false,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20
|
||||
limit: 20,
|
||||
},
|
||||
form: {
|
||||
username: "",
|
||||
name: "",
|
||||
mobile: "",
|
||||
type: 2,
|
||||
},
|
||||
dialogWork: false,
|
||||
dialogCreate: false,
|
||||
item: {},
|
||||
anchors: []
|
||||
}
|
||||
anchors: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.$axios.get('/admin/work/index', { params: this.listQuery }).then(response => {
|
||||
this.list = response.data.data
|
||||
this.total = response.data.total
|
||||
this.oss = response.ext.oss
|
||||
this.listLoading = false
|
||||
}).catch(err => {
|
||||
})
|
||||
this.$axios
|
||||
.get("/admin/liveroom/zhuboStatistics", { params: this.listQuery })
|
||||
.then((response) => {
|
||||
this.list = response.data.data;
|
||||
this.total = response.data.total;
|
||||
this.listLoading = false;
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
onDel(item) {
|
||||
this.$axios.post('/admin/work/del', {id: item.id}).then(res => {
|
||||
this.dialogVisible = false
|
||||
this.getList()
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
this.$axios
|
||||
.post("/admin/work/del", { id: item.id })
|
||||
.then((res) => {
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
onSave(from) {
|
||||
if(from) {
|
||||
this.$set(from,'date',[new Date(from.start), new Date(from.end)]);
|
||||
this.item = from;
|
||||
this.item.os = from.os+'';
|
||||
this.dialogWork = true;
|
||||
return
|
||||
}
|
||||
this.$axios.post('/admin/work/save', this.item).then(response => {
|
||||
this.dialogWork = false
|
||||
this.getList()
|
||||
}).catch(err => {
|
||||
})
|
||||
},
|
||||
onAdd() {
|
||||
this.$axios.get('/admin/work/anchor').then((res) => {
|
||||
this.anchors = res.data || []
|
||||
this.dialogCreate = true
|
||||
}).catch(err=>{
|
||||
|
||||
})
|
||||
// onSave(from) {
|
||||
// if (from) {
|
||||
// this.$set(from, "date", [new Date(from.start), new Date(from.end)]);
|
||||
// this.item = from;
|
||||
// this.item.os = from.os + "";
|
||||
// this.dialogWork = true;
|
||||
// return;
|
||||
// }
|
||||
// this.$axios
|
||||
// .post("/admin/work/save", this.item)
|
||||
// .then((response) => {
|
||||
// this.dialogWork = false;
|
||||
// this.getList();
|
||||
// })
|
||||
// .catch((err) => {});
|
||||
// },
|
||||
onAdd(item) {
|
||||
// this.anchors = res.data || [];
|
||||
this.dialogCreate = true;
|
||||
this.$nextTick((res) => {
|
||||
this.$refs["form"].resetFields();
|
||||
this.title = item.id ? "编辑" : "添加";
|
||||
this.form = item.id
|
||||
? { ...item }
|
||||
: {
|
||||
username: "",
|
||||
name: "",
|
||||
mobile: "",
|
||||
type: 2,
|
||||
};
|
||||
});
|
||||
},
|
||||
onSaves() {
|
||||
if(this.loading) return
|
||||
this.loading = true
|
||||
this.$axios.post('/admin/work/save2', this.anchors).then(res => {
|
||||
this.dialogCreate = false
|
||||
this.loading = false
|
||||
}).catch(err=> {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$axios
|
||||
.post("/admin/admin/save", this.form)
|
||||
.then((response) => {
|
||||
this.$message({
|
||||
message: "成功",
|
||||
type: "success",
|
||||
});
|
||||
this.dialogCreate = false;
|
||||
this.getList();
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -27,14 +27,14 @@
|
|||
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="getList">
|
||||
搜索
|
||||
</el-button>
|
||||
<!-- <el-button
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="getList(1)"
|
||||
>
|
||||
导出
|
||||
</el-button> -->
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
|
||||
|
@ -77,6 +77,17 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="当月核销率(按订单)" width="160">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.month_write_rate }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" width="180" label="当月核销率(按销售额)">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.month_write_rate_price }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -40,11 +40,11 @@
|
|||
<span>{{ scope.row.last_work_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="138px" align="center" label="路线">
|
||||
<!-- <el-table-column width="138px" align="center" label="线路">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.last_work_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
<el-tag type="border-card">{{ scope.row.routes }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -578,7 +578,7 @@ export default {
|
|||
flowObj: "",
|
||||
os: null, // 初始值,你可以根据需要设置为 1、2 或 3
|
||||
},
|
||||
os_arr: { 1: "美团", 2: "快手", 3: "抖音" },
|
||||
os_arr: { 1: "美团", 2: "快手", 3: "抖音(甄选)", 4:"全平台", 5:"抖音(新国旅)" },
|
||||
adminList: [],
|
||||
form: {},
|
||||
rules: {
|
||||
|
@ -628,12 +628,14 @@ export default {
|
|||
this.$axios
|
||||
.get("/admin/order/index", { params: this.listQuery })
|
||||
.then((response) => {
|
||||
console.log(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)
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.sn" placeholder="订单号" style="width: 300px;" class="filter-item" />
|
||||
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="getList">
|
||||
搜索
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
|
||||
|
||||
<el-table-column align="center" fixed width="220" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="scope.row.self ==0 && scope.row.status == 0" type="primary" size="small" icon="el-icon-check" @click="onPass(scope.row)">
|
||||
确认
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="订单号" width="80" prop="orderInfo.sn" />
|
||||
<el-table-column align="center" label="出游日期" width="80" prop="travel_date" />
|
||||
<el-table-column align="center" label="出游人数" width="80" prop="num" />
|
||||
<el-table-column align="center" label="出行人名称" width="220" prop="name" />
|
||||
<el-table-column align="center" label="联系电话" prop="mobile" />
|
||||
<el-table-column align="center" label="券码图片" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-image
|
||||
style="width: 100px; height: 100px"
|
||||
:src="scope.row.code_pic"
|
||||
:preview-src-list="[scope.row.code_pic]">
|
||||
</el-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="备注" prop="note" />
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="listQuery.page"
|
||||
:limit.sync="listQuery.limit"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import Pagination from '@/Wangeditor/Pagination'
|
||||
import Pagination from '@/components/PaginationFixed'
|
||||
|
||||
export default {
|
||||
name: 'Orderlist',
|
||||
components: { Pagination },
|
||||
filters: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
total: 0,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 10
|
||||
},
|
||||
oss: {},
|
||||
item: {},
|
||||
dialogVisible: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.$axios.get('/admin/orderbooks/list', { params: this.listQuery }).then(response => {
|
||||
this.list = response.data.data
|
||||
this.total = response.data.total
|
||||
this.oss = response.ext
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
onBack() {
|
||||
this.$axios.post('/admin/order/back', this.item).then(res => {
|
||||
this.dialogVisible = false
|
||||
this.item = {}
|
||||
this.getList()
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
},
|
||||
onPass(item) {
|
||||
this.$axios.post('/admin/order/backpass', { id: item.id }).then(res => {
|
||||
this.dialogVisible = false
|
||||
this.item = {}
|
||||
this.getList()
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.app-container {
|
||||
position: relative;
|
||||
padding-bottom: 60px; /* 分页条的高度 */
|
||||
}
|
||||
|
||||
.filter-container,
|
||||
.el-table {
|
||||
padding-bottom: 52px; /* 分页条的高度,以避免内容重叠 */
|
||||
}
|
||||
</style>
|
|
@ -39,6 +39,8 @@
|
|||
v-model="listQuery.os_status"
|
||||
placeholder="平台状态"
|
||||
:options="oss"
|
||||
clearable
|
||||
:props="{ checkStrictly: true }"
|
||||
class="filter-item"
|
||||
@change="handleChange"
|
||||
/>
|
||||
|
@ -87,13 +89,14 @@
|
|||
<el-select
|
||||
v-model="listQuery.appointment_status"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="预约状态"
|
||||
class="filter-item"
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option key="" label="请选择" value="" />
|
||||
<el-option key="1" label="已预约" value="1" />
|
||||
<el-option key="0" label="未预约" value="0" />
|
||||
<el-option key="1" label="已预约(未处理)" value="1" />
|
||||
<el-option key="2" label="已预约(已处理)" value="2" />
|
||||
</el-select>
|
||||
|
||||
<el-button
|
||||
|
@ -122,6 +125,15 @@
|
|||
>
|
||||
核单
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="multipleSelection.length == 0"
|
||||
class="filter-item"
|
||||
icon="el-icon-refresh"
|
||||
@click="onCirculationAll()"
|
||||
>
|
||||
批量流转
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
|
@ -132,7 +144,9 @@
|
|||
highlight-current-row
|
||||
style="width: 100%"
|
||||
:height="tableMaxHeight"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55"> </el-table-column>
|
||||
<el-table-column align="center" fixed width="200" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button-group>
|
||||
|
@ -167,6 +181,15 @@
|
|||
>
|
||||
同步
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-if="scope.row.appointment_status == 1"
|
||||
size="small"
|
||||
icon="el-icon-edit"
|
||||
@click="onOneClickYyHandle(scope.row)"
|
||||
>
|
||||
预约处理
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -181,7 +204,11 @@
|
|||
<el-table-column align="center" fixed label="电话" width="140">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.mobile }}</span>
|
||||
<span style="display:block;font-size: 12px;">{{ scope.row.mobileInfo.area }}-{{ scope.row.mobileInfo.originalIsp }}</span>
|
||||
<span style="display: block; font-size: 12px"
|
||||
>{{ scope.row.mobileInfo.area }}-{{
|
||||
scope.row.mobileInfo.originalIsp
|
||||
}}</span
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
@ -234,7 +261,13 @@
|
|||
}"
|
||||
type="primary"
|
||||
>
|
||||
{{ scope.row.appointment_status == 1 ? '已预约' : '未预约' }}
|
||||
{{
|
||||
scope.row.appointment_status == 1
|
||||
? "已预约(未处理)"
|
||||
: scope.row.appointment_status == 2
|
||||
? "已预约(已处理)"
|
||||
: "未预约"
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -424,7 +457,7 @@
|
|||
>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="出游日期">
|
||||
<el-form-item required pros="travel_date" label="出游日期">
|
||||
<el-date-picker
|
||||
v-model="item.travel_date"
|
||||
type="date"
|
||||
|
@ -440,7 +473,12 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="下次跟进时间" v-if="item.status !== 2">
|
||||
<el-form-item
|
||||
required
|
||||
pros="next_follow"
|
||||
label="下次跟进时间"
|
||||
v-if="item.status !== 2"
|
||||
>
|
||||
<el-date-picker
|
||||
v-model="next_follow"
|
||||
type="datetime"
|
||||
|
@ -470,7 +508,7 @@
|
|||
</el-select>
|
||||
</el-form-item>-->
|
||||
|
||||
<el-form-item label="跟进说明">
|
||||
<el-form-item required pros="desc" label="跟进说明">
|
||||
<el-input v-model="item.desc" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -538,10 +576,10 @@
|
|||
|
||||
<el-dialog title="申请转出订单" :visible.sync="applyVisible">
|
||||
<el-form label-width="160px" :model="item3" :rules="rules" ref="ruleForm">
|
||||
<el-form-item label="标题:">
|
||||
<el-form-item label="标题:" v-if="!isAll">
|
||||
<el-input v-model="item3.product_name" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="订单号:">
|
||||
<el-form-item label="订单号:" v-if="!isAll">
|
||||
<el-input v-model="item3.sn" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="流转对象:" style="width: 600px" prop="flowObj">
|
||||
|
@ -590,7 +628,7 @@
|
|||
<script>
|
||||
// import Pagination from '@/Wangeditor/Pagination'
|
||||
import Pagination from "@/components/PaginationFixed";
|
||||
import { orderBack } from "@/api/order";
|
||||
import { orderBack, orderbackBatch } from "@/api/order";
|
||||
export default {
|
||||
name: "Orderlist",
|
||||
components: { Pagination },
|
||||
|
@ -642,8 +680,9 @@ export default {
|
|||
admin: null,
|
||||
zhubo: null,
|
||||
os_status: [],
|
||||
appointment_status: "",
|
||||
},
|
||||
item: { next_follow: "", personnel: {} },
|
||||
item: { next_follow: "", personnel: { adult: "" } },
|
||||
follow: [],
|
||||
|
||||
dialogVisible: false,
|
||||
|
@ -657,9 +696,12 @@ export default {
|
|||
flowObj: "",
|
||||
os: null, // 初始值,你可以根据需要设置为 1、2 或 3
|
||||
},
|
||||
os_arr: { 1: "美团", 2: "快手", 3: "抖音" },
|
||||
os_arr: { 1: "美团", 2: "快手", 3: "抖音(甄选)", 5: "抖音(新国旅)" },
|
||||
multipleSelection: [],
|
||||
sn: [],
|
||||
adminList: [],
|
||||
form: {},
|
||||
isAll: false,
|
||||
rules: {
|
||||
flowObj: [
|
||||
{ required: true, message: "请选择流转对象", trigger: "change" },
|
||||
|
@ -673,21 +715,47 @@ export default {
|
|||
if (this.$route.query.start && this.$route.query.end) {
|
||||
this.listQuery.times = [this.$route.query.start, this.$route.query.end];
|
||||
}
|
||||
// console.log(typeof this.$route.params.id);
|
||||
this.setQuery("status");
|
||||
this.setQuery("os_status");
|
||||
this.setQuery("times");
|
||||
await this.getList();
|
||||
await this.setOneClickRepair();
|
||||
await this.getList();
|
||||
this.setQuery("appointment_status");
|
||||
// await this.getList();
|
||||
// if (!this.$route.query.id) {
|
||||
// await this.setOneClickRepair();
|
||||
// await this.getList();
|
||||
// }
|
||||
this.getShortcutContent();
|
||||
this.getAdminList();
|
||||
},
|
||||
computed: {
|
||||
tableMaxHeight() {
|
||||
return window.innerHeight - 320 + 'px';
|
||||
mounted() {
|
||||
console.log(this.$route.query);
|
||||
this.setMode();
|
||||
if (this.$route.query.id) {
|
||||
this.onInfo({ id: this.$route.query.id });
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableMaxHeight() {
|
||||
return window.innerHeight - 320 + "px";
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
this.onInfo({ id: this.$route.query.id });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSelectionChange(val) {
|
||||
// console.log(val);
|
||||
|
||||
this.multipleSelection = val;
|
||||
const data = [];
|
||||
this.multipleSelection.map((item) => {
|
||||
data.push(item.sn);
|
||||
});
|
||||
this.sn = data;
|
||||
},
|
||||
setQuery(key) {
|
||||
if (this.$route.query.hasOwnProperty(key)) {
|
||||
this.listQuery[key] = this.$route.query[key];
|
||||
|
@ -746,7 +814,7 @@ export default {
|
|||
this.value = null;
|
||||
this.next_follow = null;
|
||||
this.$set(item, "next_follow", null);
|
||||
this.item = item;
|
||||
// this.item = item;
|
||||
this.active = "follow";
|
||||
this.$axios
|
||||
.get("/admin/order/info", { params: { id: item.id } })
|
||||
|
@ -759,10 +827,10 @@ export default {
|
|||
resetForm(formName) {
|
||||
this.$refs[formName].resetFields();
|
||||
},
|
||||
getAdminList(typeDesc = '') {
|
||||
getAdminList(typeDesc = "") {
|
||||
this.$axios
|
||||
.get("/admin/admin/index", {
|
||||
params: { limit: 100, status: 1, is_order: 1, type_desc:typeDesc },
|
||||
params: { limit: 100, status: 1, is_order: 1, type_desc: typeDesc },
|
||||
})
|
||||
.then((response) => {
|
||||
this.adminList = response.data.data;
|
||||
|
@ -773,6 +841,7 @@ export default {
|
|||
onCirculation(item) {
|
||||
this.getAdminList(item.category_desc);
|
||||
this.applyVisible = true;
|
||||
this.isAll = false;
|
||||
this.item3 = { ...item, os: Number(item.os) };
|
||||
console.log(this.item3);
|
||||
if (this.item3.backs && this.item3.backs.admin_id) {
|
||||
|
@ -781,18 +850,52 @@ export default {
|
|||
this.resetForm("ruleForm");
|
||||
}
|
||||
},
|
||||
// 批量设置
|
||||
onCirculationAll() {
|
||||
this.applyVisible = true;
|
||||
this.isAll = true;
|
||||
},
|
||||
//确定
|
||||
onCirculationSave(to_admin_id) {
|
||||
this.$refs.ruleForm.validate((valid) => {
|
||||
this.$refs.ruleForm.validate(async (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();
|
||||
});
|
||||
let res;
|
||||
if (this.isAll) {
|
||||
this.$confirm("是否批量流转订单", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
res = await orderbackBatch({
|
||||
sn: this.sn,
|
||||
to_admin_id: to_admin_id,
|
||||
});
|
||||
if (res.data) {
|
||||
this.$message({
|
||||
message: "批量流转订单成功",
|
||||
type: "success",
|
||||
});
|
||||
this.applyVisible = false;
|
||||
this.isAll = false;
|
||||
this.getList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res = await orderBack({
|
||||
sn: this.item3.sn,
|
||||
os: this.item3.os,
|
||||
to_admin_id: to_admin_id,
|
||||
});
|
||||
if (res.data) {
|
||||
this.$message({
|
||||
message: "流转订单成功",
|
||||
type: "success",
|
||||
});
|
||||
this.applyVisible = false;
|
||||
this.isAll = false;
|
||||
this.getList();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -806,6 +909,7 @@ export default {
|
|||
.post("/admin/order/backcancel", { id: this.item3.id })
|
||||
.then((res) => {
|
||||
this.applyVisible = false;
|
||||
this.isAll = false;
|
||||
this.getList();
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -827,8 +931,6 @@ export default {
|
|||
.catch((err) => {});
|
||||
},
|
||||
onSave(item) {
|
||||
console.log(this.next_follow);
|
||||
|
||||
this.$axios
|
||||
.post("/admin/order/save", {
|
||||
id: item.id,
|
||||
|
@ -843,7 +945,10 @@ export default {
|
|||
})
|
||||
.then((res) => {
|
||||
this.dialogVisible = false;
|
||||
this.item = { next_follow: "", personnel: {} };
|
||||
this.item = { next_follow: "", personnel: { adult: "" } };
|
||||
this.$router.push({
|
||||
path: "/order/index",
|
||||
});
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
|
@ -890,7 +995,6 @@ export default {
|
|||
this.$axios
|
||||
.post("/admin/order/oneClickRepair", { id: item.id })
|
||||
.then((res) => {
|
||||
this.dialogVisible = false;
|
||||
this.$notify({
|
||||
title: "成功",
|
||||
message: "同步完成",
|
||||
|
@ -898,6 +1002,25 @@ export default {
|
|||
});
|
||||
// this.getList();
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$notify.error({
|
||||
title: "同步失败",
|
||||
message: err,
|
||||
});
|
||||
});
|
||||
},
|
||||
onOneClickYyHandle(item) {
|
||||
this.$axios
|
||||
.post("/admin/order/changeAppointmentStatus", { id: item.id })
|
||||
.then((res) => {
|
||||
this.dialogVisible = false;
|
||||
this.$notify({
|
||||
title: "成功",
|
||||
message: "已处理",
|
||||
type: "success",
|
||||
});
|
||||
// this.getList();
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$notify.error({
|
||||
title: "错误",
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
搜索
|
||||
</el-button>
|
||||
|
||||
<!-- <el-button
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="getList(1)"
|
||||
>
|
||||
导出
|
||||
</el-button> -->
|
||||
</el-button>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
|||
<el-tag type="warning" v-if="scope.row.os === 1">美团</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.os === 2">快手</el-tag>
|
||||
<el-tag type="primary" v-if="scope.row.os === 3">抖音</el-tag>
|
||||
<el-tag type="primary" v-if="scope.row.os === 5">抖音(新国旅)</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
@ -144,6 +145,9 @@ export default {
|
|||
}, {
|
||||
value: '3',
|
||||
label: '抖音'
|
||||
}, {
|
||||
value: '5',
|
||||
label: '抖音(新国旅)'
|
||||
}],
|
||||
form: {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,992 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input
|
||||
v-model="listQuery.sn"
|
||||
placeholder="订单号"
|
||||
style="width: 200px"
|
||||
class="filter-item"
|
||||
/>
|
||||
<el-input
|
||||
v-model="listQuery.product_name"
|
||||
placeholder="标题"
|
||||
style="width: 200px"
|
||||
class="filter-item"
|
||||
/>
|
||||
|
||||
<el-input
|
||||
v-model="listQuery.mobile"
|
||||
placeholder="手机号"
|
||||
style="width: 200px"
|
||||
class="filter-item"
|
||||
/>
|
||||
|
||||
<el-input
|
||||
v-model="listQuery.zhubo"
|
||||
placeholder="主播"
|
||||
style="width: 100px"
|
||||
class="filter-item"
|
||||
/>
|
||||
|
||||
<el-input
|
||||
v-model="listQuery.admin"
|
||||
placeholder="客服"
|
||||
style="width: 100px"
|
||||
class="filter-item"
|
||||
/>
|
||||
|
||||
<!-- <el-cascader
|
||||
v-model="listQuery.os_status"
|
||||
placeholder="平台状态"
|
||||
:options="oss"
|
||||
clearable
|
||||
:props="{ checkStrictly: true }"
|
||||
class="filter-item"
|
||||
@change="handleChange"
|
||||
/> -->
|
||||
|
||||
<el-select
|
||||
v-model="listQuery.status"
|
||||
filterable
|
||||
placeholder="跟进状态"
|
||||
class="filter-item"
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option key="" label="请选择" value="" />
|
||||
<el-option
|
||||
v-for="(v, k) in status_arr"
|
||||
:key="k"
|
||||
:label="v"
|
||||
:value="k"
|
||||
/>
|
||||
</el-select>
|
||||
|
||||
<el-select
|
||||
v-model="listQuery.timetype"
|
||||
filterable
|
||||
placeholder="时间"
|
||||
class="filter-item"
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option key="" label="请选择" value="" />
|
||||
<el-option
|
||||
v-for="(v, k) in timetype_arr"
|
||||
:key="k"
|
||||
:label="v"
|
||||
:value="k"
|
||||
/>
|
||||
</el-select>
|
||||
|
||||
<el-date-picker
|
||||
v-model="listQuery.times"
|
||||
class="filter-item"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
end-placeholder="结束日期"
|
||||
/>
|
||||
<el-select
|
||||
v-model="listQuery.appointment_status"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="预约状态"
|
||||
class="filter-item"
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option key="0" label="未预约" value="0" />
|
||||
<el-option key="1" label="已预约(未处理)" value="1" />
|
||||
<el-option key="2" label="已预约(已处理)" value="2" />
|
||||
</el-select>
|
||||
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="getList"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="getList(1)"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
|
||||
<!-- <el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="dialog2Visible = true"
|
||||
>
|
||||
核单
|
||||
</el-button> -->
|
||||
<!-- <el-button
|
||||
type="primary"
|
||||
:disabled="multipleSelection.length == 0"
|
||||
class="filter-item"
|
||||
icon="el-icon-refresh"
|
||||
@click="onCirculationAll()"
|
||||
>
|
||||
批量流转
|
||||
</el-button> -->
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
style="width: 100%"
|
||||
:height="tableMaxHeight"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55"> </el-table-column>
|
||||
<el-table-column align="center" fixed label="电话" width="140">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.mobile }}</span>
|
||||
<span style="display: block; font-size: 12px"
|
||||
>{{ scope.row.mobileInfo.area }}-{{
|
||||
scope.row.mobileInfo.originalIsp
|
||||
}}</span
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<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.appointment_status],
|
||||
border: `1px solid ${order_status[scope.row.appointment_status]}`,
|
||||
}"
|
||||
type="primary"
|
||||
>
|
||||
{{
|
||||
scope.row.appointment_status == 1
|
||||
? "已预约(未处理)"
|
||||
: scope.row.appointment_status == 2
|
||||
? "已预约(已处理)"
|
||||
: "未预约"
|
||||
}}
|
||||
</div>
|
||||
</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="setMode"
|
||||
/>
|
||||
|
||||
<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 required pros="travel_date" 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
|
||||
required
|
||||
pros="next_follow"
|
||||
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 required pros="desc" 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="标题:" v-if="!isAll">
|
||||
<el-input v-model="item3.product_name" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="订单号:" v-if="!isAll">
|
||||
<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 '@/Wangeditor/Pagination'
|
||||
import Pagination from "@/components/PaginationFixed";
|
||||
import { orderBack, orderbackBatch } 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: [],
|
||||
appointment_status: "",
|
||||
},
|
||||
item: { next_follow: "", personnel: { adult: "" } },
|
||||
follow: [],
|
||||
|
||||
dialogVisible: false,
|
||||
dialog2Visible: false,
|
||||
applyVisible: false,
|
||||
oss: [],
|
||||
isSynchronization: false,
|
||||
item3: {
|
||||
sn: null,
|
||||
backs: null,
|
||||
flowObj: "",
|
||||
os: null, // 初始值,你可以根据需要设置为 1、2 或 3
|
||||
},
|
||||
os_arr: { 1: "美团", 2: "快手", 3: "抖音(甄选)", 5: "抖音(新国旅)" },
|
||||
multipleSelection: [],
|
||||
sn: [],
|
||||
adminList: [],
|
||||
form: {},
|
||||
isAll: false,
|
||||
rules: {
|
||||
flowObj: [
|
||||
{ required: true, message: "请选择流转对象", trigger: "change" },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
async 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];
|
||||
}
|
||||
// console.log(typeof this.$route.params.id);
|
||||
this.setQuery("status");
|
||||
this.setQuery("os_status");
|
||||
this.setQuery("times");
|
||||
this.setQuery("appointment_status");
|
||||
// await this.getList();
|
||||
// if (!this.$route.query.id) {
|
||||
// await this.setOneClickRepair();
|
||||
// await this.getList();
|
||||
// }
|
||||
this.getShortcutContent();
|
||||
this.getAdminList();
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.$route.query);
|
||||
this.setMode();
|
||||
if (this.$route.query.id) {
|
||||
this.onInfo({ id: this.$route.query.id });
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableMaxHeight() {
|
||||
return window.innerHeight - 320 + "px";
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
this.onInfo({ id: this.$route.query.id });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSelectionChange(val) {
|
||||
// console.log(val);
|
||||
|
||||
this.multipleSelection = val;
|
||||
const data = [];
|
||||
this.multipleSelection.map((item) => {
|
||||
data.push(item.sn);
|
||||
});
|
||||
this.sn = data;
|
||||
},
|
||||
setQuery(key) {
|
||||
if (this.$route.query.hasOwnProperty(key)) {
|
||||
this.listQuery[key] = this.$route.query[key];
|
||||
} else {
|
||||
this.listQuery[key] = "";
|
||||
}
|
||||
},
|
||||
async setMode() {
|
||||
await this.getList();
|
||||
// await this.setOneClickRepair();
|
||||
// await this.getList();
|
||||
},
|
||||
async setOneClickRepair() {
|
||||
let arr = this.list.map((res) => {
|
||||
return res.id;
|
||||
});
|
||||
await this.onOneClickRepair({ id: arr.join() });
|
||||
},
|
||||
async getList($is_excel) {
|
||||
this.listQuery.excel = null;
|
||||
this.listQuery.is_refunded = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
await 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;
|
||||
this.isSynchronization = true;
|
||||
});
|
||||
},
|
||||
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(typeDesc = "") {
|
||||
this.$axios
|
||||
.get("/admin/admin/index", {
|
||||
params: { limit: 100, status: 1, is_order: 1, type_desc: typeDesc },
|
||||
})
|
||||
.then((response) => {
|
||||
this.adminList = response.data.data;
|
||||
this.listLoading = false;
|
||||
})
|
||||
.catch((err) => {});
|
||||
},
|
||||
onCirculation(item) {
|
||||
this.getAdminList(item.category_desc);
|
||||
this.applyVisible = true;
|
||||
this.isAll = false;
|
||||
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");
|
||||
}
|
||||
},
|
||||
// 批量设置
|
||||
onCirculationAll() {
|
||||
this.applyVisible = true;
|
||||
this.isAll = true;
|
||||
},
|
||||
//确定
|
||||
onCirculationSave(to_admin_id) {
|
||||
this.$refs.ruleForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
let res;
|
||||
if (this.isAll) {
|
||||
this.$confirm("是否批量流转订单", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
res = await orderbackBatch({
|
||||
sn: this.sn,
|
||||
to_admin_id: to_admin_id,
|
||||
});
|
||||
if (res.data) {
|
||||
this.$message({
|
||||
message: "批量流转订单成功",
|
||||
type: "success",
|
||||
});
|
||||
this.applyVisible = false;
|
||||
this.isAll = false;
|
||||
this.getList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res = await orderBack({
|
||||
sn: this.item3.sn,
|
||||
os: this.item3.os,
|
||||
to_admin_id: to_admin_id,
|
||||
});
|
||||
if (res.data) {
|
||||
this.$message({
|
||||
message: "流转订单成功",
|
||||
type: "success",
|
||||
});
|
||||
this.applyVisible = false;
|
||||
this.isAll = 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.isAll = 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) {
|
||||
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: { adult: "" } };
|
||||
this.$router.push({
|
||||
path: "/order/index",
|
||||
});
|
||||
})
|
||||
.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(() => {});
|
||||
},
|
||||
onOneClickRepair(item) {
|
||||
// this.$axios
|
||||
// .post("/admin/order/oneClickRepair", { id: item.id })
|
||||
// .then((res) => {
|
||||
// this.$notify({
|
||||
// title: "成功",
|
||||
// message: "同步完成",
|
||||
// type: "success",
|
||||
// });
|
||||
// // this.getList();
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// this.$notify.error({
|
||||
// title: "同步失败",
|
||||
// message: err,
|
||||
// });
|
||||
// });
|
||||
},
|
||||
onOneClickYyHandle(item) {
|
||||
this.$axios
|
||||
.post("/admin/order/changeAppointmentStatus", { id: item.id })
|
||||
.then((res) => {
|
||||
this.dialogVisible = false;
|
||||
this.$notify({
|
||||
title: "成功",
|
||||
message: "已处理",
|
||||
type: "success",
|
||||
});
|
||||
// this.getList();
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$notify.error({
|
||||
title: "错误",
|
||||
message: err,
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
position: relative;
|
||||
padding-bottom: 60px; /* 分页条的高度 */
|
||||
}
|
||||
|
||||
.filter-container,
|
||||
.el-table {
|
||||
padding-bottom: 5px; /* 分页条的高度,以避免内容重叠 */
|
||||
}
|
||||
</style>
|
|
@ -578,7 +578,7 @@ export default {
|
|||
flowObj: "",
|
||||
os: null, // 初始值,你可以根据需要设置为 1、2 或 3
|
||||
},
|
||||
os_arr: { 1: "美团", 2: "快手", 3: "抖音" },
|
||||
os_arr: { 1: "美团", 2: "快手", 3: "抖音(甄选)", 5:"抖音(新国旅)"},
|
||||
adminList: [],
|
||||
form: {},
|
||||
rules: {
|
||||
|
|
|
@ -3,9 +3,19 @@
|
|||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="problem_form">
|
||||
<el-form :inline="true" ref="form" :model="dataForm" label-width="60px">
|
||||
<el-form
|
||||
:inline="true"
|
||||
ref="form"
|
||||
:model="dataForm"
|
||||
label-width="60px"
|
||||
>
|
||||
<el-form-item label="关键字:">
|
||||
<el-input v-model="dataForm.keyword" placeholder="请输入搜索关键字" style="width: 400px;" class="filter-item" />
|
||||
<el-input
|
||||
v-model="dataForm.keyword"
|
||||
placeholder="请输入搜索关键字"
|
||||
style="width: 400px"
|
||||
class="filter-item"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="success" @click="onSubmit">查询</el-button>
|
||||
|
@ -15,26 +25,58 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
<div class="problem_container">
|
||||
<div class="problem_left" v-infinite-scroll="load" :infinite-scroll-immediate="false" style="overflow:auto;padding: 10px;">
|
||||
<div @click="handleQacityl(item.city_id)" class="btn" v-for="item in getQaCityList">{{ item.city_name }}</div>
|
||||
<div
|
||||
class="problem_left"
|
||||
v-infinite-scroll="load"
|
||||
:infinite-scroll-immediate="false"
|
||||
style="overflow: auto; padding: 10px"
|
||||
>
|
||||
<div
|
||||
@click="handleQacityl(item.city_id)"
|
||||
class="btn"
|
||||
v-for="item in getQaCityList"
|
||||
>
|
||||
{{ item.city_name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="problem_right">
|
||||
<ul class="infinite-list" v-infinite-scroll="load" :infinite-scroll-immediate="false" style="overflow:auto">
|
||||
<ul
|
||||
class="infinite-list"
|
||||
v-infinite-scroll="load"
|
||||
:infinite-scroll-immediate="false"
|
||||
style="overflow: auto"
|
||||
>
|
||||
<li class="problem_right_container" v-for="item in getQaLists">
|
||||
<div class="title">
|
||||
<span v-html="handleprant(item.title)"></span>
|
||||
<!-- <el-button @click="handleZip(item.img_zip)" type="primary">下载图片</el-button>
|
||||
<el-button @click="handleZip(item.trip_zip)" type="primary">下载行程</el-button> -->
|
||||
<el-button @click="showImgDialog(item.img_zip)" type="primary">下载图片</el-button>
|
||||
<el-button @click="showFileList(item.trip_zip)" type="primary" style="margin-left: 10px;">下载行程</el-button>
|
||||
<el-button @click="showImgDialog(item.img_zip)" type="primary"
|
||||
>下载图片</el-button
|
||||
>
|
||||
<el-button
|
||||
@click="showFileList(item.trip_zip)"
|
||||
type="primary"
|
||||
style="margin-left: 10px"
|
||||
>下载行程</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="desc_container" v-for="list in item.qaQuestions">
|
||||
<span style="font-weight: 700;color: #46a6ff;" class="desc" v-html="handleprant(list.title)">
|
||||
<span
|
||||
style="font-weight: 700; color: #46a6ff"
|
||||
class="desc"
|
||||
v-html="handleprant(list.title)"
|
||||
>
|
||||
</span>
|
||||
<el-button @click="copyToClipboard(list.content)" type="primary" size="mini" style="margin-left: 10px;">复制</el-button>
|
||||
<el-button
|
||||
@click="copyToClipboard(list.content)"
|
||||
type="primary"
|
||||
size="mini"
|
||||
style="margin-left: 10px"
|
||||
>复制</el-button
|
||||
>
|
||||
|
||||
<div class="desc" v-html="handleprantHtml(list.content)">
|
||||
</div>
|
||||
<div class="desc" v-html="handleprantHtml(list.content)"></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -50,10 +92,18 @@
|
|||
class="image-card"
|
||||
:body-style="{ padding: '10px' }"
|
||||
>
|
||||
<img :src="image.url" class="image-preview" />
|
||||
<div style="text-align: center">{{ image.desc }}</div>
|
||||
<img :src="image.file" class="image-preview" />
|
||||
<div class="image-footer">
|
||||
<el-button @click="handlePreview(image.url)" size="mini">查看</el-button>
|
||||
<el-button @click="handleDownload(image.url)" type="success" size="mini">下载</el-button>
|
||||
<el-button @click="handlePreview(image.file)" size="mini"
|
||||
>查看</el-button
|
||||
>
|
||||
<el-button
|
||||
@click="handleDownload(image.file)"
|
||||
type="success"
|
||||
size="mini"
|
||||
>下载</el-button
|
||||
>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
@ -72,14 +122,22 @@
|
|||
class="file-card"
|
||||
:body-style="{ padding: '10px' }"
|
||||
>
|
||||
<div class="file-info">
|
||||
<div class="file-info">
|
||||
<el-icon class="file-icon">
|
||||
<i :class="getFileIcon(file.url)"></i>
|
||||
<i :class="getFileIcon(file.file)"></i>
|
||||
</el-icon>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<span class="file-name" style="display: block;">{{ getFileName(file.file) }}</span>
|
||||
</div>
|
||||
<div class="file-info">
|
||||
<span class="file-name">{{ file.desc }}</span>
|
||||
</div>
|
||||
<div class="file-footer">
|
||||
<el-button @click="handleDownload(file.url)" type="success" size="mini">下载</el-button>
|
||||
<el-button
|
||||
@click="handleDownload(file.file)"
|
||||
type="success"
|
||||
size="mini"
|
||||
>下载</el-button
|
||||
>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
@ -99,7 +157,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { getQaCityList, getQaList } from '@/api/qa'
|
||||
import { getQaCityList, getQaList } from "@/api/qa";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -107,110 +165,119 @@ export default {
|
|||
getQaCityList: [],
|
||||
getQaLists: [],
|
||||
dataForm: {
|
||||
keyword: '',
|
||||
city_id: ''
|
||||
keyword: "",
|
||||
city_id: "",
|
||||
},
|
||||
dialogImage: false, // 控制图片列表对话框的显示
|
||||
previewVisible: false, // 控制图片预览对话框的显示
|
||||
previewImageUrl: '', // 预览图片的 URL
|
||||
previewTitle: '', // 预览图片的标题
|
||||
imageList: [ // 示例图片列表
|
||||
previewImageUrl: "", // 预览图片的 URL
|
||||
previewTitle: "", // 预览图片的标题
|
||||
imageList: [
|
||||
// 示例图片列表
|
||||
],
|
||||
fileList: [ // 示例图片列表
|
||||
fileList: [
|
||||
// 示例图片列表
|
||||
],
|
||||
dialogFile: false,
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
getQaCityList().then(res => {
|
||||
this.getQaCityList = res.data
|
||||
})
|
||||
getQaCityList().then((res) => {
|
||||
this.getQaCityList = res.data;
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
'dataForm.keyword': function (newVal) {
|
||||
"dataForm.keyword": function (newVal) {
|
||||
if (newVal) {
|
||||
this.onSubmit();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 显示文件列表对话框
|
||||
showFileList(fileUrl) {
|
||||
this.fileList = [];
|
||||
if (fileUrl) {
|
||||
this.fileList = fileUrl.split(',').map(url => ({ url, name: url.split('/').pop() }));
|
||||
if (fileUrl.length) {
|
||||
this.fileList = fileUrl;
|
||||
}
|
||||
this.dialogFile = true;
|
||||
},
|
||||
// 根据文件 URL 获取文件图标
|
||||
getFileIcon(url) {
|
||||
const ext = url.split('.').pop().toLowerCase();
|
||||
const ext = url.split(".").pop().toLowerCase();
|
||||
console.log('ext:' + ext);
|
||||
switch (ext) {
|
||||
case 'pdf':
|
||||
return 'el-icon-file-pdf';
|
||||
case 'docx':
|
||||
return 'el-icon-file-word';
|
||||
case 'pptx':
|
||||
return 'el-icon-file-ppt';
|
||||
case 'xlsx':
|
||||
return 'el-icon-file-excel';
|
||||
case "pdf":
|
||||
return "el-icon-file-pdf";
|
||||
case "docx":
|
||||
return "el-icon-file-word";
|
||||
case "pptx":
|
||||
return "el-icon-file-ppt";
|
||||
case "xlsx":
|
||||
return "el-icon-file-excel";
|
||||
default:
|
||||
return 'el-icon-file';
|
||||
return "el-icon-file";
|
||||
}
|
||||
},
|
||||
// 根据文件 URL 获取文件图标
|
||||
getFileName(url) {
|
||||
return url.substring(url.lastIndexOf("/") + 1);
|
||||
},
|
||||
handleQacityl(val) {
|
||||
getQaList({ city_id: val }).then(res => {
|
||||
this.getQaLists = res.data.data
|
||||
})
|
||||
getQaList({ city_id: val }).then((res) => {
|
||||
this.getQaLists = res.data.data;
|
||||
});
|
||||
},
|
||||
handleZip(url) {
|
||||
if (url) {
|
||||
// window.open(url)
|
||||
fetch(url)
|
||||
.then(response => response.blob())
|
||||
.then(blob => {
|
||||
const link = document.createElement('a');
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const link = document.createElement("a");
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
link.href = objectUrl;
|
||||
link.download = url.split('/').pop(); // Extract filename from URL
|
||||
link.download = url.split("/").pop(); // Extract filename from URL
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '下载失败',
|
||||
type: 'error'
|
||||
message: "下载失败",
|
||||
type: "error",
|
||||
});
|
||||
console.error('Download error:', error);
|
||||
console.error("Download error:", error);
|
||||
});
|
||||
} else {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '暂无下载链接',
|
||||
type: 'warning'
|
||||
message: "暂无下载链接",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
},
|
||||
load() {
|
||||
console.log('load')
|
||||
console.log("load");
|
||||
},
|
||||
handleprant(val) {
|
||||
if (!val) return ''; // 处理空值情况,避免返回 undefined 或其他非字符串值
|
||||
if (!val) return ""; // 处理空值情况,避免返回 undefined 或其他非字符串值
|
||||
|
||||
let replaceReg = new RegExp(this.dataForm.keyword, "ig");
|
||||
let replaceString = `<span style="color: #fff;background-color: #FC0421FF;">${this.dataForm.keyword}</span>`;
|
||||
return val.replace(replaceReg, replaceString);
|
||||
},
|
||||
handleprantHtml(val) {
|
||||
if (!val) return '';
|
||||
if (!val) return "";
|
||||
|
||||
let p = this.dataForm.keyword;
|
||||
let regexp = new RegExp(p, "g");
|
||||
let replacedHtml = val.replace(/(?<=>)[^>]+(?=<[/]?\w+.*>)/g, (v) => {
|
||||
return v.replace(regexp, `<span style='color: #fff;background-color: #FC0421FF;'>${p}</span>`);
|
||||
return v.replace(
|
||||
regexp,
|
||||
`<span style='color: #fff;background-color: #FC0421FF;'>${p}</span>`
|
||||
);
|
||||
});
|
||||
// 检查是否有替换发生
|
||||
if (replacedHtml === val) {
|
||||
|
@ -220,32 +287,31 @@ export default {
|
|||
return replacedHtml;
|
||||
},
|
||||
onSubmit() {
|
||||
getQaList(this.dataForm).then(res => {
|
||||
this.getQaLists = res.data.data
|
||||
})
|
||||
getQaList(this.dataForm).then((res) => {
|
||||
this.getQaLists = res.data.data;
|
||||
});
|
||||
},
|
||||
copyToClipboard(html) {
|
||||
const text = this.stripHtml(html);
|
||||
const input = document.createElement('textarea');
|
||||
const input = document.createElement("textarea");
|
||||
input.value = text;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(input);
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '内容已复制'
|
||||
message: "内容已复制",
|
||||
});
|
||||
},
|
||||
stripHtml(html) {
|
||||
const div = document.createElement('div');
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML = html;
|
||||
return div.textContent || div.innerText || '';
|
||||
return div.textContent || div.innerText || "";
|
||||
},
|
||||
showImgDialog(imagesUrls) {
|
||||
this.imageList = []
|
||||
if (imagesUrls) {
|
||||
this.imageList = imagesUrls.split(',').map(url => ({ url }));
|
||||
if (imagesUrls.length) {
|
||||
this.imageList = imagesUrls;
|
||||
}
|
||||
|
||||
this.dialogImage = true;
|
||||
|
@ -253,7 +319,7 @@ export default {
|
|||
// 处理图片预览
|
||||
handlePreview(url) {
|
||||
this.previewImageUrl = url;
|
||||
this.previewTitle = '图片预览';
|
||||
this.previewTitle = "图片预览";
|
||||
this.previewVisible = true;
|
||||
},
|
||||
// 处理图片删除
|
||||
|
@ -265,35 +331,35 @@ export default {
|
|||
if (url) {
|
||||
// window.open(url)
|
||||
fetch(url)
|
||||
.then(response => response.blob())
|
||||
.then(blob => {
|
||||
const link = document.createElement('a');
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const link = document.createElement("a");
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
link.href = objectUrl;
|
||||
link.download = url.split('/').pop(); // Extract filename from URL
|
||||
link.download = url.split("/").pop(); // Extract filename from URL
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '下载失败',
|
||||
type: 'error'
|
||||
message: "下载失败",
|
||||
type: "error",
|
||||
});
|
||||
console.error('Download error:', error);
|
||||
console.error("Download error:", error);
|
||||
});
|
||||
} else {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '暂无下载链接',
|
||||
type: 'warning'
|
||||
message: "暂无下载链接",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -365,7 +431,7 @@ body {
|
|||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
color: #46a6ff;
|
||||
&>:nth-child(1) {
|
||||
& > :nth-child(1) {
|
||||
margin-right: 40px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-edit"
|
||||
@click="onEdit(scope.row)"
|
||||
@click="onAdd(scope.row)"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
|
@ -96,264 +96,185 @@
|
|||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog title="添加QA" :visible.sync="dialogCreate">
|
||||
<el-form label-width="120px" :model="anchors">
|
||||
<el-form-item label="城市">
|
||||
<el-select v-model="anchors.city_id" placeholder="请选择">
|
||||
<el-form-item
|
||||
style="display: inline-flex; text-align: left; width: 770px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in getQaCitys"
|
||||
:key="item.city_id"
|
||||
style="display: inline-flex; word-break: break-all"
|
||||
:label="item.city_name"
|
||||
:value="item.city_id"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="旅游路线">
|
||||
<el-input
|
||||
v-model="anchors.title"
|
||||
type="text"
|
||||
placeholder="请输入旅游路线"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="QA内容">
|
||||
<div
|
||||
class="mistake-content"
|
||||
v-for="(item, index) in anchors.qaQuestions"
|
||||
>
|
||||
<div class="mistake-left">
|
||||
<div>副标题</div>
|
||||
<div class="qa-desc">
|
||||
<el-input
|
||||
style="width: 100px; margin-right: 10px"
|
||||
v-model="item.sort"
|
||||
type="text"
|
||||
placeholder="序号"
|
||||
/>
|
||||
<el-input
|
||||
v-model="item.title"
|
||||
type="text"
|
||||
placeholder="请输入副标题"
|
||||
/>
|
||||
</div>
|
||||
<div>内容</div>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<myEditor v-model="item.content" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mistake-right">
|
||||
<el-button @click="handleDel(index)" type="danger"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mistake-btn">
|
||||
<el-button type="primary" @click="handleAdd">添加</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-switch
|
||||
v-model="anchors.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传图片">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="admin/upload/index"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
>
|
||||
<div v-for="(item, index) in anchors.img_zip" class="img-box">
|
||||
<i
|
||||
@click.stop="handleClose('img_zip', index)"
|
||||
class="close el-icon-close"
|
||||
/>
|
||||
<i v-if="!checkIfUrlContainsImage(item)" class="el-icon-folder" />
|
||||
<img
|
||||
v-else
|
||||
style="width: 100px; height: 100px"
|
||||
:src="item"
|
||||
class="avatar"
|
||||
alt=""
|
||||
/>
|
||||
<div class="desc">{{ handleRegex(item) }}</div>
|
||||
</div>
|
||||
<i class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
<div style="color: red">(请上传.jpg, png的图片)</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传行程">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="admin/upload/index"
|
||||
:show-file-list="false"
|
||||
:on-success="handleSuccess"
|
||||
>
|
||||
<div v-for="(item, index) in anchors.trip_zip" class="img-box">
|
||||
<i
|
||||
@click.stop="handleClose('trip_zip', index)"
|
||||
class="close el-icon-close"
|
||||
/>
|
||||
<i v-if="!checkIfUrlContainsImage(item)" class="el-icon-folder" />
|
||||
<img
|
||||
v-else
|
||||
style="width: 100px; height: 100px"
|
||||
:src="item"
|
||||
class="avatar"
|
||||
alt=""
|
||||
/>
|
||||
<div class="desc">{{ handleRegex(item) }}</div>
|
||||
</div>
|
||||
<i class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
<span style="color: red">(本行程请上传,ppt,word,pdf格式的文件)</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-dialog
|
||||
v-if="dialogCreate"
|
||||
ref="dialog"
|
||||
:title="title"
|
||||
:visible.sync="dialogCreate"
|
||||
>
|
||||
<div
|
||||
style="display: flex; justify-content: flex-end; margin-bottom: 10px"
|
||||
>
|
||||
<el-button v-loading="loading" type="primary" @click="onSave"
|
||||
>保 存</el-button
|
||||
>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="编辑内容" :visible.sync="dialogEdit">
|
||||
<el-form label-width="120px" :model="anchors">
|
||||
<el-form-item label="城市">
|
||||
<el-select v-model="anchors.city_id" placeholder="请选择">
|
||||
<el-form-item
|
||||
style="display: inline-flex; text-align: left; width: 770px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in getQaCitys"
|
||||
:key="item.city_id"
|
||||
style="
|
||||
width: 250px;
|
||||
display: inline-flex;
|
||||
word-break: break-all;
|
||||
"
|
||||
:label="item.city_name"
|
||||
:value="item.city_id"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="旅游路线">
|
||||
<el-input
|
||||
v-model="anchors.title"
|
||||
type="text"
|
||||
placeholder="请输入旅游路线"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="QA内容">
|
||||
<div
|
||||
class="mistake-content"
|
||||
v-for="(item, index) in anchors.qaQuestions"
|
||||
>
|
||||
<div class="mistake-left">
|
||||
<div>副标题</div>
|
||||
<div class="qa-desc">
|
||||
<el-input
|
||||
style="width: 100px; margin-right: 10px"
|
||||
v-model="item.sort"
|
||||
type="text"
|
||||
placeholder="序号"
|
||||
/>
|
||||
<el-input
|
||||
v-model="item.title"
|
||||
type="text"
|
||||
placeholder="请输入副标题"
|
||||
/>
|
||||
</div>
|
||||
<div>内容</div>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<myEditor v-model="item.content" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mistake-right">
|
||||
<el-button @click="handleDel(index)" type="danger"
|
||||
>删除</el-button
|
||||
<el-scrollbar ref="scrollbar" class="scrollable-container">
|
||||
<el-form ref="addForm" label-width="120px" :model="anchors">
|
||||
<el-form-item label="城市" prop="city_id">
|
||||
<el-select v-model="anchors.city_id" placeholder="请选择">
|
||||
<el-form-item
|
||||
style="display: inline-flex; text-align: left; width: 770px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in getQaCitys"
|
||||
:key="item.city_id"
|
||||
style="display: inline-flex; word-break: break-all"
|
||||
:label="item.city_name"
|
||||
:value="item.city_id"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="旅游路线" prop="title">
|
||||
<el-input
|
||||
v-model="anchors.title"
|
||||
type="text"
|
||||
placeholder="请输入旅游路线"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="QA内容">
|
||||
<div
|
||||
class="mistake-content"
|
||||
v-for="(item, index) in anchors.qaQuestions"
|
||||
>
|
||||
<div class="mistake-left">
|
||||
<div>副标题</div>
|
||||
<div class="qa-desc">
|
||||
<el-input
|
||||
style="width: 100px; margin-right: 10px"
|
||||
v-model="item.sort"
|
||||
type="text"
|
||||
placeholder="序号"
|
||||
/>
|
||||
<el-input
|
||||
v-model="item.title"
|
||||
type="text"
|
||||
placeholder="请输入副标题"
|
||||
/>
|
||||
</div>
|
||||
<div>内容</div>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<myEditor v-model="item.content" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mistake-right">
|
||||
<el-button @click="handleDel(index)" type="danger"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mistake-btn">
|
||||
<el-button type="primary" @click="handleAdd">添加</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-switch
|
||||
v-model="anchors.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传图片">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="admin/upload/index"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
<div class="mistake-btn">
|
||||
<el-button type="primary" @click="handleAdd">添加</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-switch
|
||||
v-model="anchors.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10px;
|
||||
"
|
||||
>
|
||||
<div v-for="(item, index) in anchors.img_zip" class="img-box">
|
||||
<i
|
||||
@click.stop="handleClose('img_zip', index)"
|
||||
class="close el-icon-close"
|
||||
/>
|
||||
<i v-if="!checkIfUrlContainsImage(item)" class="el-icon-folder" />
|
||||
<img
|
||||
v-else
|
||||
style="width: 100px; height: 100px"
|
||||
:src="item"
|
||||
class="avatar"
|
||||
alt=""
|
||||
/>
|
||||
<div class="desc">{{ handleRegex(item) }}</div>
|
||||
<el-button type="primary" @click="onAddImg()">添加图片</el-button>
|
||||
</div>
|
||||
<el-form-item label="上传图片">
|
||||
<div class="upload-list">
|
||||
<div class="wu-yu" v-for="(item, index) in anchors.img_zip">
|
||||
<i
|
||||
@click.stop="handleClose('img_zip', index)"
|
||||
class="close el-icon-close"
|
||||
/>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action=""
|
||||
:show-file-list="false"
|
||||
:http-request="handlesAvatarSuccess"
|
||||
:on-success="
|
||||
(response, file, fileList) =>
|
||||
handleAvatarSuccess(response, file, fileList, index)
|
||||
"
|
||||
>
|
||||
<div v-if="!!item.file" class="img-box">
|
||||
<i
|
||||
v-if="!checkIfUrlContainsImage(item.file)"
|
||||
class="el-icon-folder"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
style="width: 100px; height: 100px"
|
||||
:src="item.file"
|
||||
class="avatar"
|
||||
alt=""
|
||||
/>
|
||||
<div class="desc">{{ handleRegex(item.file) }}</div>
|
||||
</div>
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
<el-input v-model="item.desc" placeholder="图片说明"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<i class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
<div style="color: red">(请上传.jpg, png的图片)</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传行程">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="admin/upload/index"
|
||||
:show-file-list="false"
|
||||
:on-success="handleSuccess"
|
||||
<div style="color: red">(请上传.jpg, png的图片)</div>
|
||||
</el-form-item>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10px;
|
||||
"
|
||||
>
|
||||
<div v-for="(item, index) in anchors.trip_zip" class="img-box">
|
||||
<i
|
||||
@click.stop="handleClose('trip_zip', index)"
|
||||
class="close el-icon-close"
|
||||
/>
|
||||
<i v-if="!checkIfUrlContainsImage(item)" class="el-icon-folder" />
|
||||
<img
|
||||
v-else
|
||||
style="width: 100px; height: 100px"
|
||||
:src="item"
|
||||
class="avatar"
|
||||
alt=""
|
||||
/>
|
||||
<div class="desc">{{ handleRegex(item) }}</div>
|
||||
<el-button type="primary" @click="onAddtrip()">添加行程</el-button>
|
||||
</div>
|
||||
<el-form-item label="上传行程">
|
||||
<div class="upload-list">
|
||||
<div class="wu-yu" v-for="(item, index) in anchors.trip_zip">
|
||||
<i
|
||||
@click.stop="handleClose('trip_zip', index)"
|
||||
class="close el-icon-close"
|
||||
/>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action=""
|
||||
:http-request="handlesAvatarSuccess"
|
||||
:show-file-list="false"
|
||||
:on-success="
|
||||
(response, file, fileList) =>
|
||||
handleSuccess(response, file, fileList, index)
|
||||
"
|
||||
>
|
||||
<div v-if="!!item.file" class="img-box">
|
||||
<i
|
||||
v-if="!checkIfUrlContainsImage(item.file)"
|
||||
class="el-icon-folder"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
style="width: 100px; height: 100px"
|
||||
:src="item.file"
|
||||
class="avatar"
|
||||
alt=""
|
||||
/>
|
||||
<div class="desc">{{ handleRegex(item.file) }}</div>
|
||||
</div>
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
<el-input v-model="item.desc" placeholder="行程说明"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<i class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
<span style="color: red">(本行程请上传,ppt,word,pdf格式的文件)</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button v-loading="loading" type="primary" @click="onSave"
|
||||
>保 存</el-button
|
||||
>
|
||||
</div>
|
||||
<span style="color: red"
|
||||
>(本行程请上传,ppt,word,pdf格式的文件)</span
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -361,13 +282,14 @@
|
|||
<script>
|
||||
import Pagination from "@/components/PaginationFixed";
|
||||
import myEditor from "@/components/Wangeditor/index.vue";
|
||||
|
||||
import { getToken } from "@/utils/auth";
|
||||
export default {
|
||||
name: "getQa",
|
||||
components: { Pagination, myEditor },
|
||||
data() {
|
||||
return {
|
||||
statusArr: { 0: "禁用", 1: "启用" },
|
||||
title: "",
|
||||
list: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
|
@ -387,8 +309,8 @@ export default {
|
|||
item: {},
|
||||
anchors: {
|
||||
qaQuestions: [],
|
||||
img_zip: [],
|
||||
trip_zip: [],
|
||||
img_zip: [{ desc: "", file: "" }],
|
||||
trip_zip: [{ desc: "", file: "" }],
|
||||
},
|
||||
getQaCitys: {},
|
||||
};
|
||||
|
@ -411,19 +333,42 @@ export default {
|
|||
".svg",
|
||||
".webp",
|
||||
];
|
||||
console.log(url);
|
||||
console.log(
|
||||
"========fffff====" +
|
||||
imageExtensions.some((extension) =>
|
||||
url.toLowerCase().endsWith(extension)
|
||||
)
|
||||
);
|
||||
console.log(url);
|
||||
return imageExtensions.some((extension) =>
|
||||
url.toLowerCase().endsWith(extension)
|
||||
);
|
||||
},
|
||||
|
||||
async handlesAvatarSuccess(file) {
|
||||
try {
|
||||
var formdata = new FormData();
|
||||
formdata.append("file", file.file);
|
||||
|
||||
this.upLoading = true;
|
||||
const _this = this;
|
||||
const res = await this.$axios.post("/admin/upload/index", formdata, {
|
||||
headers: {
|
||||
"Content-type": "multipart/form-data",
|
||||
"X-Token": getToken(),
|
||||
},
|
||||
});
|
||||
// if (![200].includes(res.data.code)) {
|
||||
// this.$message.error("上传失败");
|
||||
// return;
|
||||
// }
|
||||
// 主动回调success方法
|
||||
file.onSuccess(res);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleClose(val, i) {
|
||||
if (this.anchors[val].length == 1) {
|
||||
this.$message({
|
||||
message: "至少保留一条",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
console.log("i===", i);
|
||||
this.anchors[val].splice(i, 1);
|
||||
},
|
||||
|
@ -461,19 +406,27 @@ export default {
|
|||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
handleAvatarSuccess(res, file) {
|
||||
console.log("====1111===" + res);
|
||||
this.anchors.img_zip.push(
|
||||
`${window.location.protocol}//${window.location.host}${res.data}`
|
||||
);
|
||||
console.log("====111122222===" + this.anchors.img_zip);
|
||||
onAddImg() {
|
||||
this.anchors.img_zip.push({ desc: "", file: "" });
|
||||
},
|
||||
handleSuccess(res, file) {
|
||||
console.log("====222222===" + res);
|
||||
this.anchors.trip_zip.push(
|
||||
`${window.location.protocol}//${window.location.host}${res.data}`
|
||||
);
|
||||
console.log("====111122222===" + this.anchors.trip_zip);
|
||||
onAddtrip() {
|
||||
this.anchors.trip_zip.push({ desc: "", file: "" });
|
||||
},
|
||||
handleAvatarSuccess(res, file, fileList, index) {
|
||||
if (!res.data) return;
|
||||
this.anchors.img_zip[
|
||||
index
|
||||
].file = `${window.location.protocol}//${window.location.host}${res.data}`;
|
||||
// this.anchors.img_zip.push(
|
||||
// `${window.location.protocol}//${window.location.host}${res.data}`
|
||||
// );
|
||||
},
|
||||
handleSuccess(res, file, fileList, index) {
|
||||
console.log(res, file, fileList);
|
||||
if (!res.data) return;
|
||||
this.anchors.trip_zip[
|
||||
index
|
||||
].file = `${window.location.protocol}//${window.location.host}${res.data}`;
|
||||
},
|
||||
handleRegex(val) {
|
||||
const regex = /\/([^\/]+)$/;
|
||||
|
@ -484,51 +437,54 @@ export default {
|
|||
this.listQuery.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
onAdd() {
|
||||
this.anchors.qaQuestions = [
|
||||
{
|
||||
sort: 1,
|
||||
title: "",
|
||||
content: "",
|
||||
},
|
||||
];
|
||||
// 初始化时默认排序值为0
|
||||
this.anchors.img_zip = [];
|
||||
this.anchors.trip_zip = [];
|
||||
onAdd(item) {
|
||||
this.dialogCreate = true;
|
||||
},
|
||||
onEdit(item) {
|
||||
if (!item.qaQuestions.length) {
|
||||
this.anchors = {
|
||||
...item,
|
||||
qaQuestions: [
|
||||
{
|
||||
sort: 1,
|
||||
title: "",
|
||||
content: "",
|
||||
},
|
||||
],
|
||||
};
|
||||
if (!item.id) {
|
||||
this.title = "添加QA";
|
||||
this.anchors.qaQuestions = [
|
||||
{
|
||||
sort: 1,
|
||||
title: "",
|
||||
content: "",
|
||||
},
|
||||
];
|
||||
// 初始化时默认排序值为0
|
||||
this.anchors.img_zip = [{ desc: "", file: "" }];
|
||||
this.anchors.trip_zip = [{ desc: "", file: "" }];
|
||||
} else {
|
||||
this.anchors = { ...item };
|
||||
this.title = "编辑QA";
|
||||
if (!item.qaQuestions.length) {
|
||||
this.anchors = {
|
||||
...item,
|
||||
qaQuestions: [
|
||||
{
|
||||
sort: 1,
|
||||
title: "",
|
||||
content: "",
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
this.anchors = { ...item };
|
||||
}
|
||||
this.anchors.img_zip = item.img_zip
|
||||
? item.img_zip
|
||||
: [{ desc: "", file: "" }];
|
||||
this.anchors.trip_zip = item.trip_zip
|
||||
? item.trip_zip
|
||||
: [{ desc: "", file: "" }];
|
||||
}
|
||||
this.anchors.img_zip = item.img_zip;
|
||||
this.anchors.trip_zip = item.trip_zip;
|
||||
// this.anchors.img_zip =
|
||||
// typeof item.img_zip == "string" ? [item.img_zip] : item.img_zip;
|
||||
// this.anchors.trip_zip =
|
||||
// typeof item.trip_zip == "string" ? [item.trip_zip] : item.trip_zip;
|
||||
this.dialogEdit = true;
|
||||
},
|
||||
onSave() {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
const api = this.dialogCreate ? "/admin/qa/addQa" : "/admin/qa/editQa";
|
||||
const api =
|
||||
this.title == "添加QA" ? "/admin/qa/addQa" : "/admin/qa/editQa";
|
||||
this.$axios
|
||||
.post(api, this.anchors)
|
||||
.then(() => {
|
||||
this.dialogCreate = false;
|
||||
this.dialogEdit = false;
|
||||
// this.dialogEdit = false;
|
||||
this.loading = false;
|
||||
this.getList();
|
||||
})
|
||||
|
@ -585,11 +541,45 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scrollable-container {
|
||||
// position: relative;
|
||||
height: 500px; /* 可以根据实际情况调整高度 */
|
||||
// overflow-y: auto;
|
||||
}
|
||||
::v-deep.el-scrollbar .el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.upload-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.wu-yu {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 110px;
|
||||
flex-wrap: wrap;
|
||||
align-content: space-between;
|
||||
& + .wu-yu {
|
||||
margin-left: 5px;
|
||||
}
|
||||
::v-deep.el-input {
|
||||
width: 100px;
|
||||
margin: 10px 0 10px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.filter-items {
|
||||
position: fixed;
|
||||
// right: 0;
|
||||
left: 70%;
|
||||
z-index: 66;
|
||||
}
|
||||
.img-box {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 10px;
|
||||
& + .img-box {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
@ -615,7 +605,7 @@ export default {
|
|||
.close {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -8px;
|
||||
right: -15px;
|
||||
font-size: 18px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<div class="scheduling_head">
|
||||
<div class="head_lable">产品编号:</div>
|
||||
<div
|
||||
class="scheduling_tab"
|
||||
@click="handleClick(index)"
|
||||
:key="index"
|
||||
:class="activeName == item.id ? 'active' : ''"
|
||||
v-for="(item, index) in liveroomList"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<el-button type="primary" style="margin-left: 210px" @click="handleEdit"
|
||||
>编辑</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="scheduling_tab_pana">
|
||||
<div class="table-list" v-for="item in roomWorksList">
|
||||
<div class="table-list_item">
|
||||
<div class="tit">
|
||||
{{ item.date }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-list_item">
|
||||
<div class="tit">
|
||||
{{ item.date_name
|
||||
}}<i
|
||||
@click="handleAdds('', item.date)"
|
||||
class="el-icon-circle-plus"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-list-box" v-for="it in item.work">
|
||||
<div class="border" @dblclick="handleAdds(it, item.date)">
|
||||
<div style="margin-bottom: 10px">
|
||||
AM {{ extractTime(it.start) }}~{{ extractTime(it.end) }}
|
||||
</div>
|
||||
<div>主播:{{ it.zhubo.name }}</div>
|
||||
<div>中控:{{ it.zhongkong.name }}</div>
|
||||
<div>路线:{{ it.route ? it.route : "无" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<liveroom-update
|
||||
v-if="addOrUpdateVisible"
|
||||
ref="addOrUpdate"
|
||||
@update="saveProducts"
|
||||
></liveroom-update>
|
||||
<roomWorksUpdate
|
||||
v-if="roomVisible"
|
||||
ref="roomUpdate"
|
||||
:title="title"
|
||||
@update="saveRoomWorks"
|
||||
></roomWorksUpdate>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
liveroom,
|
||||
roomWorks,
|
||||
availableZhubo,
|
||||
saveProducts,
|
||||
saveRoomWorks,
|
||||
} from "@/api/scheduling";
|
||||
import liveroomUpdate from "./liveroom-update.vue";
|
||||
import roomWorksUpdate from "./room-works";
|
||||
export default {
|
||||
name: "Scheduling",
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
activeName: "",
|
||||
listLoading: true,
|
||||
title: "添加排班",
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
title: "",
|
||||
status: "",
|
||||
},
|
||||
total: 0,
|
||||
dialogVisible: false,
|
||||
dialogType: "new",
|
||||
liveroomList: [],
|
||||
roomWorksList: [],
|
||||
product_ids: "",
|
||||
live_room_id: "",
|
||||
addOrUpdateVisible: false,
|
||||
roomVisible: false,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
liveroomUpdate,
|
||||
roomWorksUpdate,
|
||||
},
|
||||
async mounted() {
|
||||
await this.liveroom();
|
||||
this.roomWorks();
|
||||
},
|
||||
methods: {
|
||||
extractTime(dateTimeStr) {
|
||||
// 定义正则表达式来匹配时间部分
|
||||
const regex = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/;
|
||||
const match = dateTimeStr.match(regex);
|
||||
|
||||
if (!match) {
|
||||
throw new Error("不匹配");
|
||||
}
|
||||
|
||||
// 提取时间部分
|
||||
return `${match[4]}:${match[5]}:${match[6]}`;
|
||||
},
|
||||
isCurrentTimeAfter(startTimeStr) {
|
||||
// 将开始时间字符串转换为Date对象
|
||||
const startTime = new Date(startTimeStr);
|
||||
|
||||
// 获取当前时间
|
||||
const currentTime = new Date();
|
||||
|
||||
// 比较当前时间和开始时间
|
||||
return currentTime >= startTime;
|
||||
},
|
||||
// 直播间列表
|
||||
async liveroom() {
|
||||
try {
|
||||
let { data } = await liveroom();
|
||||
this.liveroomList = data;
|
||||
this.activeName = this.liveroomList[0].id + "";
|
||||
this.live_room_id = this.liveroomList[0].id;
|
||||
this.product_ids = this.liveroomList[0].product_ids;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
},
|
||||
// 排班列表
|
||||
async roomWorks() {
|
||||
try {
|
||||
let { data } = await roomWorks(this.live_room_id);
|
||||
this.roomWorksList = data;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
},
|
||||
// tab切换
|
||||
handleClick(i) {
|
||||
let { id, product_ids } = this.liveroomList[i];
|
||||
this.live_room_id = id;
|
||||
this.product_ids = product_ids;
|
||||
this.activeName = id + "";
|
||||
this.roomWorks();
|
||||
},
|
||||
// 编辑直播间
|
||||
handleEdit() {
|
||||
this.addOrUpdateVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["addOrUpdate"].init(this.live_room_id, this.product_ids);
|
||||
});
|
||||
},
|
||||
// 添加排班
|
||||
handleAdds(form, date) {
|
||||
console.log(form);
|
||||
|
||||
this.title = form.id ? "编辑排班" : "添加排班";
|
||||
if (form.id && this.isCurrentTimeAfter(form.start)) {
|
||||
return this.$message.error("当前时间无法编辑");
|
||||
}
|
||||
this.roomVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["roomUpdate"].init(this.live_room_id, { ...form }, date);
|
||||
});
|
||||
},
|
||||
saveProducts(form) {
|
||||
this.liveroom();
|
||||
saveProducts(form).then((res) => {
|
||||
this.$message({
|
||||
message: "直播间编辑成功",
|
||||
type: "success",
|
||||
});
|
||||
});
|
||||
},
|
||||
saveRoomWorks(form) {
|
||||
saveRoomWorks(form).then((res) => {
|
||||
this.roomWorks();
|
||||
this.$message({
|
||||
message: "添加排班成功",
|
||||
type: "success",
|
||||
});
|
||||
});
|
||||
},
|
||||
handleAdd() {},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
position: relative;
|
||||
padding-bottom: 60px; /* 分页条的高度 */
|
||||
}
|
||||
::v-deep .el-tabs__nav-wrap::after {
|
||||
display: none;
|
||||
}
|
||||
.filter-container,
|
||||
.el-table {
|
||||
padding-bottom: 5px; /* 分页条的高度,以避免内容重叠 */
|
||||
}
|
||||
::v-deep .el-row {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
.head_lable {
|
||||
font-size: 14px;
|
||||
color: #3d3d3d;
|
||||
font-weight: 700;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.scheduling_head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.scheduling_tab {
|
||||
display: inline-block;
|
||||
padding: 12px 28px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
& + .scheduling_tab {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
border-color: #1269ff;
|
||||
}
|
||||
.el-icon-circle-plus {
|
||||
color: #367af0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.scheduling_tab_pana {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
// align-items: center;
|
||||
.table-list {
|
||||
flex-basis: 14.3%;
|
||||
.table-list_item {
|
||||
height: 56px;
|
||||
background: #e4ecff;
|
||||
display: flex;
|
||||
// justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #d7ddeb;
|
||||
margin-right: -1px;
|
||||
margin-bottom: -1px;
|
||||
.tit {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
}
|
||||
.table-list-box {
|
||||
padding: 15px;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
border: 1px solid #d7ddeb;
|
||||
margin-right: -1px;
|
||||
margin-bottom: -1px;
|
||||
.border {
|
||||
padding: 13px;
|
||||
background: #367af0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
title="编辑直播间"
|
||||
:visible.sync="dialogVisible"
|
||||
width="30%"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<el-form ref="form" :model="form" label-width="80px">
|
||||
<el-form-item label="直播间id" prop="product_ids">
|
||||
<el-input v-model="form.product_ids"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
form: {
|
||||
product_ids: "",
|
||||
live_room_id: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init(live_room_id, product_ids) {
|
||||
this.dialogVisible = true;
|
||||
this.form.product_ids = product_ids;
|
||||
this.form.live_room_id = live_room_id;
|
||||
console.log(this.form);
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
handleEdit() {
|
||||
this.dialogVisible = false;
|
||||
this.$emit("update", this.form);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,208 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="dialogVisible"
|
||||
width="30%"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<el-form :rules="rules" ref="form" :model="form" label-width="80px">
|
||||
<el-form-item label="直播间" prop="live_room_id">
|
||||
<el-select v-model="form.live_room_id" placeholder="请选择直播间">
|
||||
<el-option
|
||||
v-for="item in liveroomList"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="开始时间" prop="start">
|
||||
<el-date-picker
|
||||
@change="handleDate"
|
||||
v-model="form.start"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetime"
|
||||
placeholder="选择日期时间"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束时间" prop="end">
|
||||
<el-date-picker
|
||||
v-model="form.end"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetime"
|
||||
@change="handleDate"
|
||||
placeholder="选择日期时间"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="时间">
|
||||
<el-time-select
|
||||
placeholder="起始时间"
|
||||
v-model="startTime"
|
||||
:picker-options="{
|
||||
start: '08:00',
|
||||
step: '00:15',
|
||||
end: '24:00',
|
||||
}"
|
||||
>
|
||||
</el-time-select>
|
||||
<el-time-select
|
||||
placeholder="结束时间"
|
||||
@change="handleDate"
|
||||
v-model="endTime"
|
||||
:picker-options="{
|
||||
start: '08:00',
|
||||
step: '00:15',
|
||||
end: '24:00',
|
||||
minTime: startTime,
|
||||
}"
|
||||
>
|
||||
</el-time-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="主播" prop="zhubo_id">
|
||||
<el-select v-model="form.zhubo_id" placeholder="请选择主播">
|
||||
<el-option
|
||||
v-for="item in zhuboList"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:disabled="item.disabled"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="中控" prop="zhongkong_id">
|
||||
<el-select v-model="form.zhongkong_id" placeholder="请选择中控">
|
||||
<el-option
|
||||
v-for="item in zhongkongList"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:disabled="item.disabled"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="路线">
|
||||
<el-input v-model="form.route"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { availableZhubo, liveroom } from "@/api/scheduling";
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: "添加排班",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
startTime: "",
|
||||
endTime: "",
|
||||
form: {
|
||||
live_room_id: "",
|
||||
start: "",
|
||||
end: "",
|
||||
zhubo_id: "",
|
||||
zhongkong_id: "",
|
||||
route: "",
|
||||
},
|
||||
zhuboList: [],
|
||||
zhongkongList: [],
|
||||
liveroomList: [],
|
||||
dayDate: "",
|
||||
rules: {
|
||||
live_room_id: [
|
||||
{ required: true, message: "请选择直播间", trigger: "change" },
|
||||
],
|
||||
zhongkong_id: [
|
||||
{ required: true, message: "请选择中控", trigger: "change" },
|
||||
],
|
||||
zhubo_id: [
|
||||
{ required: true, message: "请选择主播", trigger: "change" },
|
||||
],
|
||||
// date: [{ required: true, message: "请选择时间", trigger: "change" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
form: function () {
|
||||
this.handleDate();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
extractTime(dateTimeStr) {
|
||||
// 定义正则表达式来匹配时间部分
|
||||
const regex = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/;
|
||||
const match = dateTimeStr.match(regex);
|
||||
|
||||
if (!match) {
|
||||
throw new Error("不匹配");
|
||||
}
|
||||
|
||||
// 提取时间部分
|
||||
return `${match[4]}:${match[5]}`;
|
||||
},
|
||||
init(live_room_id, form, date) {
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick((res) => {
|
||||
this.$refs["form"].resetFields();
|
||||
this.dayDate = date;
|
||||
this.form = !!form.id
|
||||
? form
|
||||
: {
|
||||
live_room_id: "",
|
||||
start: "",
|
||||
end: "",
|
||||
zhubo_id: "",
|
||||
zhongkong_id: "",
|
||||
};
|
||||
this.form.live_room_id = live_room_id;
|
||||
this.startTime = form.id ? this.extractTime(this.form.start) : "";
|
||||
this.endTime = form.id ? this.extractTime(this.form.end) : "";
|
||||
console.log(this.startTime);
|
||||
console.log(this.endTime);
|
||||
});
|
||||
this.liveroom();
|
||||
},
|
||||
// 直播间列表
|
||||
async liveroom() {
|
||||
try {
|
||||
let { data } = await liveroom();
|
||||
this.liveroomList = data;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
handleDate() {
|
||||
this.form.end = `${this.dayDate} ${this.endTime}:00`;
|
||||
this.form.start = `${this.dayDate} ${this.startTime}:00`;
|
||||
if (this.form.end && this.form.start) {
|
||||
availableZhubo({ ...this.form, type: 2 }).then((res) => {
|
||||
this.zhuboList = res.data;
|
||||
});
|
||||
availableZhubo({ ...this.form, type: 3 }).then((res) => {
|
||||
this.zhongkongList = res.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
handleEdit() {
|
||||
// this.form.end = `${this.dayDate} ${this.startTime}:00`;
|
||||
// this.form.start = `${this.dayDate} ${this.endTime}:00`;
|
||||
this.dialogVisible = false;
|
||||
this.$emit("update", this.form);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
"/dev-api": {
|
||||
// 接口地址 以 api开头的都走下面的配置
|
||||
// target: 'https://www.szjinao.cn', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
|
||||
target: "http://192.168.0.100:8787", // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
|
||||
target: 'http://192.168.0.100:8787', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
|
||||
ws: true, // 是否支持 websocket 请求 支持
|
||||
changeOrigin: true, // 是否启用跨域
|
||||
pathRewrite: {
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace app\admin\controller;
|
|||
use app\model\Admins;
|
||||
use app\model\Onlines;
|
||||
use app\model\Orders;
|
||||
use app\model\Products;
|
||||
use support\Log;
|
||||
use support\Request;
|
||||
use support\Redis;
|
||||
|
@ -26,8 +27,8 @@ class AdminController extends base
|
|||
}
|
||||
|
||||
if($typeDesc = $request->get('type_desc')) {
|
||||
$routeType = self::ROUTE_LISTS[mb_substr($typeDesc, 0, 2)] ?? 10;
|
||||
$query->where('route_type', $routeType);
|
||||
//$routeType = self::ROUTE_LISTS[mb_substr($typeDesc, 0, 2)] ?? 10;
|
||||
//$query->where('route_type', $routeType);
|
||||
}
|
||||
|
||||
$list = $query->paginate($request->get('limit',10));
|
||||
|
@ -72,6 +73,11 @@ class AdminController extends base
|
|||
$is_franchisee = $request->post('is_franchisee',0);
|
||||
$product_ids = $request->post('product_ids','');
|
||||
$routeType = $request->post('route_type','10');
|
||||
$type = $request->post('type','0');
|
||||
|
||||
if (in_array($type, [2, 3])) { // 主播/中控默认密码:123456
|
||||
$password = '123456';
|
||||
}
|
||||
|
||||
if($id) {
|
||||
$item = (new Admins())->find($id);
|
||||
|
@ -112,6 +118,7 @@ class AdminController extends base
|
|||
$item->is_franchisee = $is_franchisee;
|
||||
$item->product_ids = $product_ids;
|
||||
$item->route_type = $routeType;
|
||||
$item->type = $type;
|
||||
Log::info('logs:' . json_encode($item));
|
||||
$back = $item->save();
|
||||
if($back)
|
||||
|
@ -177,6 +184,11 @@ class AdminController extends base
|
|||
$list[$adminId]['order_num'] = $user->order_num;
|
||||
$list[$adminId]['id'] = $adminId;
|
||||
$list[$adminId]['username'] = $user->username;
|
||||
$list[$adminId]['route_type'] = $user->route_type;
|
||||
$list[$adminId]['routes'] = [];
|
||||
if ($user->product_ids) {
|
||||
$list[$adminId]['routes'] = Products::query()->field(['product_name'])->whereIn('id', explode(',', $user->product_ids))->select();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success(array_values($list));
|
||||
|
|
|
@ -11,10 +11,16 @@ use support\Request;
|
|||
|
||||
class DataController extends base
|
||||
{
|
||||
public function index(Request $request) {
|
||||
$times = $request->get('times');
|
||||
$os = $request->get('os');
|
||||
|
||||
/**
|
||||
* 获取订单统计数据
|
||||
* @param $times
|
||||
* @param $os
|
||||
* @return Orders[]|array|\think\Collection|\think\db\Query[]|\think\model\Collection
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function orderStatics($times, $os) {
|
||||
$query = Orders::alias('o')->with('admin')
|
||||
->leftJoin('admins a','a.id = o.admin_id')
|
||||
->order('o.admin_id','desc')
|
||||
|
@ -23,19 +29,19 @@ class DataController extends base
|
|||
count(o.id) as orders,
|
||||
SUM(o.total_price) as total_price,
|
||||
SUM(CASE
|
||||
WHEN (o.os = 1 AND o.order_status = 4) OR (o.os = 3 AND o.order_status = 2) OR (o.os = 2 AND o.order_status = 5) THEN 1
|
||||
WHEN (o.os in (1,7) AND o.order_status = 4) OR (o.os in(3,5) AND o.order_status = 2) OR (o.os = 2 AND o.order_status = 5) THEN 1
|
||||
ELSE 0
|
||||
END) as assets,
|
||||
SUM(CASE
|
||||
WHEN (o.os = 1 AND o.order_status = 4) OR (o.os = 3 AND o.order_status = 2) OR (o.os = 2 AND o.order_status = 5) THEN asset_price
|
||||
WHEN (o.os in (1,7) AND o.order_status = 4) OR (o.os in(3,5) AND o.order_status = 2) OR (o.os = 2 AND o.order_status = 5) THEN asset_price
|
||||
ELSE 0
|
||||
END) as asset_price,
|
||||
SUM(CASE
|
||||
WHEN (o.os = 1 AND o.order_status = 5) OR (o.os = 3 AND o.order_status = 4) OR (o.os = 2 AND o.order_status = 1) THEN 1
|
||||
WHEN (o.os in (1,7) AND o.order_status = 5) OR (o.os in(3,5) AND o.order_status = 4) OR (o.os = 2 AND o.order_status = 1) THEN 1
|
||||
ELSE 0
|
||||
END) AS nopays,
|
||||
SUM(CASE
|
||||
WHEN (o.os = 1 AND o.order_status = 5) OR (o.os = 3 AND o.order_status = 4) OR (o.os = 2 AND o.order_status = 1) THEN actual_price
|
||||
WHEN (o.os in (1,7) AND o.order_status = 5) OR (o.os in(3,5) AND o.order_status = 4) OR (o.os = 2 AND o.order_status = 1) THEN actual_price
|
||||
ELSE 0
|
||||
END) AS nopay_price,
|
||||
o.admin_id,a.name');
|
||||
|
@ -50,8 +56,12 @@ class DataController extends base
|
|||
$query->where('o.os', $os);
|
||||
}
|
||||
|
||||
$list = $query->select();
|
||||
|
||||
return $query->select();
|
||||
}
|
||||
public function index(Request $request) {
|
||||
$times = $request->get('times');
|
||||
$os = $request->get('os');
|
||||
$list = $this->orderStatics($times, $os);
|
||||
//统计每一列
|
||||
$totalArr[1] = [
|
||||
'write_rate' => 0,
|
||||
|
@ -63,6 +73,10 @@ class DataController extends base
|
|||
'nopays' => 0,
|
||||
'nopay_price' => 0,
|
||||
'admin_id' => 0,
|
||||
'm_orders' => 0,
|
||||
'm_total_price' => 0,
|
||||
'm_assets' => 0,
|
||||
'm_asset_price' => 0,
|
||||
'name' => '合计',
|
||||
'admin' => [
|
||||
'username' => '合计',
|
||||
|
@ -70,9 +84,29 @@ class DataController extends base
|
|||
'avatar' => '',
|
||||
]
|
||||
];
|
||||
|
||||
// 当月数据统计
|
||||
$currentList = $this->orderStatics([date('Y-m-01'), date('Y-m-d H:i:s')], $os);
|
||||
$currentList = array_column($currentList->toArray(), null, 'admin_id');
|
||||
foreach ($list as $k => $v) {
|
||||
$list[$k]['write_rate'] = number_format(($v['assets']/$v['orders'])*100,2);
|
||||
$list[$k]['write_rate_price'] = number_format(($v['asset_price']/$v['total_price'])*100,2);
|
||||
|
||||
// 当月核销率(按订单)
|
||||
$list[$k]['month_write_rate'] = 0;
|
||||
// 当月核销率(按销售额)
|
||||
$list[$k]['month_write_rate_price'] = 0;
|
||||
if (isset($currentList[$v['admin_id']])) {
|
||||
$currentAdmin = $currentList[$v['admin_id']];
|
||||
$list[$k]['month_write_rate'] = number_format(($currentAdmin['assets']/$currentAdmin['orders'])*100,2);
|
||||
$list[$k]['month_write_rate_price'] = number_format(($currentAdmin['asset_price']/$currentAdmin['total_price'])*100,2);
|
||||
|
||||
$totalArr[1]['m_orders'] += $currentAdmin['orders'];
|
||||
$totalArr[1]['m_total_price'] += $currentAdmin['total_price'];
|
||||
$totalArr[1]['m_assets'] += $currentAdmin['assets'];
|
||||
$totalArr[1]['m_asset_price'] += $currentAdmin['asset_price'];
|
||||
}
|
||||
|
||||
$totalArr[1]['orders'] += $list[$k]['orders'];
|
||||
$totalArr[1]['total_price'] += $list[$k]['total_price'];
|
||||
$totalArr[1]['assets'] += $list[$k]['assets'];
|
||||
|
@ -82,6 +116,8 @@ class DataController extends base
|
|||
}
|
||||
$totalArr[1]['write_rate'] = (float)number_format(($totalArr[1]['assets']/$totalArr[1]['orders'])*100,2);
|
||||
$totalArr[1]['write_rate_price'] = (float)number_format(($totalArr[1]['asset_price']/$totalArr[1]['total_price'])*100,2);
|
||||
$totalArr[1]['month_write_rate'] = (float)number_format(($totalArr[1]['m_assets']/$totalArr[1]['m_orders'])*100,2);
|
||||
$totalArr[1]['month_write_rate_price'] = (float)number_format(($totalArr[1]['m_asset_price']/$totalArr[1]['m_total_price'])*100,2);
|
||||
$list = array_merge($list->toArray(), $totalArr);
|
||||
|
||||
$excel = $request->get('excel');
|
||||
|
|
|
@ -64,7 +64,34 @@ class IndexController extends base
|
|||
$backs = Backs::where('admin', $request->admin->id)->where('status', 0)->count();
|
||||
}
|
||||
|
||||
return $this->success(['new' => $new ?? 0, 'follow' => $follow, 'back' => $backs]);
|
||||
// 核销提醒
|
||||
$orderWriteOff = Redis::get('CRM:USER:WRITE:OFF:' . $request->admin->id);
|
||||
Redis::del('CRM:USER:WRITE:OFF:' . $request->admin->id);
|
||||
|
||||
// 跟进提醒
|
||||
$followMessage = 0;
|
||||
$followOrderId = '';
|
||||
$appointmentNum = 0;
|
||||
if ($request->admin->id > 1) {
|
||||
$start = strtotime(date('Y-m-d')) * 1000;
|
||||
$end = strtotime(date('Y-m-d', strtotime('+1 days'))) * 1000 - 1;
|
||||
$followMessageList = Orders::where(['admin_id' => $request->admin->id])
|
||||
->whereBetween('next_follow', [$start, $end])
|
||||
->where('status', 1)
|
||||
->field(['id'])
|
||||
->select();
|
||||
$followMessage = count($followMessageList);
|
||||
if ($followMessage) {
|
||||
$followOrderId = $followMessageList[0]['id'];
|
||||
}
|
||||
|
||||
// 预约未处理数量
|
||||
$appointmentNum = Orders::where(['admin_id' => $request->admin->id])
|
||||
->where('appointment_status', 1)
|
||||
->count();
|
||||
}
|
||||
|
||||
return $this->success(['new' => $new ?? 0, 'follow' => $follow, 'back' => $backs, 'order_write_off' => $orderWriteOff, 'follow_message' => $followMessage, 'follow_order_id' => $followOrderId, 'appointment_num' => $appointmentNum]);
|
||||
}
|
||||
|
||||
public function line()
|
||||
|
@ -120,51 +147,51 @@ class IndexController extends base
|
|||
SUM(total_price) as total,
|
||||
COUNT(id) as `all`,
|
||||
SUM(CASE
|
||||
WHEN (os = 1 AND order_status = 3) OR (os = 2 AND order_status = 4) OR (os = 3 AND order_status = 1) THEN 1
|
||||
WHEN (os in(1,7) AND order_status = 3) OR (os = 2 AND order_status = 4) OR (os in (3,5) AND order_status = 1) THEN 1
|
||||
ELSE 0
|
||||
END) as tobeused,
|
||||
SUM(CASE
|
||||
WHEN (os = 1 AND order_status = 3) OR (os = 2 AND order_status = 4) OR (os = 3 AND order_status = 1) THEN total_price
|
||||
WHEN (os in(1,7) AND order_status = 3) OR (os = 2 AND order_status = 4) OR (os in (3,5) AND order_status = 1) THEN total_price
|
||||
ELSE 0
|
||||
END) as tobeused_price,
|
||||
SUM(CASE
|
||||
WHEN (os = 1 AND order_status = 4) OR (os = 3 AND order_status = 2) OR (os = 2 AND order_status = 5) THEN 1
|
||||
WHEN (os in(1,7) AND order_status = 4) OR (os in (3,5) AND order_status = 2) OR (os = 2 AND order_status = 5) THEN 1
|
||||
ELSE 0
|
||||
END) as asset,
|
||||
SUM(CASE
|
||||
WHEN (os = 1 AND order_status = 4) OR (os = 3 AND order_status = 2) OR (os = 2 AND order_status = 5) THEN asset_price
|
||||
WHEN (os in(1,7) AND order_status = 4) OR (os in (3,5) AND order_status = 2) OR (os = 2 AND order_status = 5) THEN asset_price
|
||||
ELSE 0
|
||||
END) 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 in(1,7) AND status = 5) OR (os in (3,5) 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 in(1,7) AND status = 5) OR (os in (3,5) AND status = 4) OR (os = 2 AND status = 1)) THEN actual_price
|
||||
ELSE 0
|
||||
END) AS refund_price,
|
||||
SUM(CASE
|
||||
WHEN ((os=1 AND order_status=3) OR (os=2 AND order_status=4) OR (os=3 AND order_status=1)) AND create_at >= ' . $startOfThirtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
|
||||
WHEN ((os in(1,7) AND order_status=3) OR (os=2 AND order_status=4) OR (os in (3,5) AND order_status=1)) AND create_at >= ' . $startOfThirtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
|
||||
ELSE 0
|
||||
END) AS tobeused_30,
|
||||
SUM(CASE
|
||||
WHEN ((os=1 AND order_status=3) OR (os=2 AND order_status=4) OR (os=3 AND order_status=1)) AND create_at >= ' . $startOfThirtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN total_price
|
||||
WHEN ((os in(1,7) AND order_status=3) OR (os=2 AND order_status=4) OR (os in (3,5) AND order_status=1)) AND create_at >= ' . $startOfThirtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN total_price
|
||||
ELSE 0
|
||||
END) AS tobeused_price_30,
|
||||
SUM(CASE
|
||||
WHEN ((os=1 AND order_status=3) OR (os=2 AND order_status=4) OR (os=3 AND order_status=1)) AND create_at >= ' . $startOfSixtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
|
||||
WHEN ((os in(1,7) AND order_status=3) OR (os=2 AND order_status=4) OR (os in (3,5) AND order_status=1)) AND create_at >= ' . $startOfSixtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
|
||||
ELSE 0
|
||||
END) AS tobeused_60,
|
||||
SUM(CASE
|
||||
WHEN ((os=1 AND order_status=3) OR (os=2 AND order_status=4) OR (os=3 AND order_status=1)) AND create_at >= ' . $startOfSixtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN total_price
|
||||
WHEN ((os in(1,7) AND order_status=3) OR (os=2 AND order_status=4) OR (os in (3,5) AND order_status=1)) AND create_at >= ' . $startOfSixtyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN total_price
|
||||
ELSE 0
|
||||
END) AS tobeused_price_60,
|
||||
SUM(CASE
|
||||
WHEN ((os=1 AND order_status=3) OR (os=2 AND order_status=4) OR (os=3 AND order_status=1)) AND create_at >= ' . $startOfEightyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
|
||||
WHEN ((os in(1,7) AND order_status=3) OR (os=2 AND order_status=4) OR (os in (3,5) AND order_status=1)) AND create_at >= ' . $startOfEightyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN 1
|
||||
ELSE 0
|
||||
END) AS tobeused_80,
|
||||
SUM(CASE
|
||||
WHEN ((os=1 AND order_status=3) OR (os=2 AND order_status=4) OR (os=3 AND order_status=1)) AND create_at >= ' . $startOfEightyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN total_price
|
||||
WHEN ((os in(1,7) AND order_status=3) OR (os=2 AND order_status=4) OR (os in (3,5) AND order_status=1)) AND create_at >= ' . $startOfEightyDaysAgoTimestamp . ' AND create_at <= ' . $endOfDayTimestamp . ' THEN total_price
|
||||
ELSE 0
|
||||
END) AS tobeused_price_80
|
||||
');
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\model\Admins;
|
||||
use app\model\LiveRoom;
|
||||
use app\model\LiveRoomWorks;
|
||||
use app\model\Orders;
|
||||
use app\model\Works;
|
||||
use support\Request;
|
||||
|
||||
class LiveRoomController extends base {
|
||||
/**
|
||||
* 直播间列表
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function index(Request $request) {
|
||||
$all = LiveRoom::select();
|
||||
return $this->success($all);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新直播产品
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function saveProducts(Request $request) {
|
||||
$roomId = $request->post('live_room_id', '');
|
||||
if (empty($roomId)) {
|
||||
$this->error('请选择直播间');
|
||||
}
|
||||
$products = $request->post('product_ids', '');
|
||||
$productIds = explode(',', $products);
|
||||
foreach ($productIds as $productId) {
|
||||
if (empty($productId)) {
|
||||
continue;
|
||||
}
|
||||
$liveRoom = LiveRoom::whereRaw(sprintf('find_in_set(%s, `product_ids`)', $productId))->where('id', '!=', $roomId)->find();
|
||||
if (!empty($liveRoom)) {
|
||||
return $this->error(sprintf('产品ID:%s已经在%s存在', $productId, $liveRoom['name']));
|
||||
}
|
||||
}
|
||||
LiveRoom::where(['id' => $roomId])->update(['product_ids' => $products]);
|
||||
return $this->success([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排班列表
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
*/
|
||||
public function roomWorks(Request $request)
|
||||
{
|
||||
$roomId = $request->get('live_room_id', '');
|
||||
if (!$roomId) {
|
||||
return $this->error('请选择直播间');
|
||||
}
|
||||
$weekDays = get_week_days();
|
||||
foreach ($weekDays as &$day) {
|
||||
$day['work'] = LiveRoomWorks::getDayWork($day['date'], $roomId);
|
||||
}
|
||||
|
||||
return $this->success($weekDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排班
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
*/
|
||||
public function saveRoomWorks(Request $request)
|
||||
{
|
||||
$id = $request->post('id', '0');
|
||||
$roomId = $request->post('live_room_id', '');
|
||||
$zhuboId = $request->post('zhubo_id', 0);
|
||||
$zhongkongId = $request->post('zhongkong_id', 0);
|
||||
$route = $request->post('route', '');
|
||||
$start = $request->post('start');
|
||||
$end = $request->post('end');
|
||||
$month = date('Ym', strtotime($start));
|
||||
$day = date('Ymd', strtotime($start));
|
||||
|
||||
if (strtotime($start) < time()) {
|
||||
return $this->error(6000, '当前时间不可排班');
|
||||
}
|
||||
|
||||
if (strtotime($start) >= strtotime($end)) {
|
||||
return $this->error(6000, '开始时间不可大于结束时间');
|
||||
}
|
||||
|
||||
// 检测当前日期是否已有排班
|
||||
if (LiveRoomWorks::dayWorkIsExists($id, $roomId, $start, $end)) {
|
||||
return $this->error(6001, '当前时间段已有排班');
|
||||
}
|
||||
// 检测当前主播当天是否已排班
|
||||
if (LiveRoomWorks::zhuboWorkIsExists($id, $zhuboId, $day)) {
|
||||
return $this->error(6002, '当前主播已排班');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'live_room_id' => $roomId,
|
||||
'zhubo_id' => $zhuboId,
|
||||
'zhongkong_id' => $zhongkongId,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'month' => $month,
|
||||
'day' => $day,
|
||||
'route' => $route,
|
||||
];
|
||||
if ($id) {
|
||||
$result = LiveRoomWorks::where('id', $id)->update($data);
|
||||
} else {
|
||||
$result = LiveRoomWorks::create($data);
|
||||
}
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可排班主播列表
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function availableZhubo(Request $request) {
|
||||
$type = $request->get('type', 2);
|
||||
$id = $request->get('id', 0);
|
||||
$start = $request->get('start');
|
||||
if (empty($start)) {
|
||||
return $this->error('请选择时间');
|
||||
}
|
||||
$end = $request->get('end');
|
||||
$day = date('Ymd', strtotime($start));
|
||||
|
||||
$query = Admins::where('status', 1)->where('type', $type);
|
||||
$list = $query->field(['id', 'name', 'mobile'])->select();
|
||||
|
||||
// 获取已排班主播
|
||||
$adminIds = LiveRoomWorks::inWorkAdmin($day)->column('zhubo_id');
|
||||
foreach ($list as $val) {
|
||||
$val->disabled = false;
|
||||
if ($type == 2 && in_array($val->id, $adminIds)) {
|
||||
$val->disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主播业绩查询
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
*/
|
||||
public function zhuboStatistics(Request $request)
|
||||
{
|
||||
$start = $request->get('start', date('Y-m-01'));
|
||||
$end = $request->get('end', date('Y-m-d'));
|
||||
$times = $request->get('times');
|
||||
if (!empty($times) && is_array($times)) {
|
||||
$start = $times[0];
|
||||
$end = $times[1];
|
||||
}
|
||||
// 姓名,职务,手机号,订单金额,直播时长
|
||||
$mobile = $request->get('mobile', '');
|
||||
$query = Admins::whereIn('type', [2, 3]);
|
||||
if (!empty($mobile)) {
|
||||
$query->where('mobile', $mobile);
|
||||
}
|
||||
|
||||
$list = $query->order('id', 'desc')->paginate($request->get('limit', 10));
|
||||
foreach ($list as $work) {
|
||||
$work->orders = 0;
|
||||
$work->total = 0;
|
||||
$work->asset_total = 0;
|
||||
$work->work_time = 0;
|
||||
|
||||
$statics = LiveRoomWorks::staticOrder($work->type, $work->id, $start, $end);
|
||||
if ($statics) {
|
||||
$work->orders = $statics->orders;
|
||||
$work->total = $statics->total/100;
|
||||
$work->asset_total = $statics->asset_total/100;
|
||||
$work->work_time = $statics->work_time;
|
||||
}
|
||||
}
|
||||
return $this->success($list);
|
||||
}
|
||||
|
||||
public function test() {
|
||||
$data = LiveRoomWorks::staticOrder(3, 38, '2024-09-17 16:00:00', '2024-09-18 15:59:59');
|
||||
return $this->success($data);
|
||||
}
|
||||
}
|
|
@ -4,11 +4,13 @@ require_once(__DIR__.'/xlsxwriter.class.php');
|
|||
|
||||
use app\model\Admins;
|
||||
use app\model\Backs;
|
||||
use app\model\Blacks;
|
||||
use app\model\Follows;
|
||||
use app\model\Orders;
|
||||
use app\model\Logs;
|
||||
use app\model\ThirdMobileLogs;
|
||||
use app\server\AliCloudApiService;
|
||||
use app\server\SMS;
|
||||
use app\server\ThirdApiService;
|
||||
use app\server\Orders as ServerOrders;
|
||||
use stdClass;
|
||||
use support\Log;
|
||||
|
@ -24,11 +26,15 @@ class OrderController extends base
|
|||
$timetype = $request->get('timetype');
|
||||
$times = $request->get('times');
|
||||
$os_status = $request->get('os_status');
|
||||
$isRefunded = $request->get('is_refunded', 0);
|
||||
|
||||
$where = [];
|
||||
if(!$request->admin->is_super) {
|
||||
$where = [['admin_id','=', $request->admin->id]];
|
||||
} else {
|
||||
$where = [['admin_id', '>', 0]];
|
||||
}
|
||||
$where[] = ['is_refunded', '=', $isRefunded];
|
||||
|
||||
if($sn) {
|
||||
$where[] = ['sn','=', $sn];
|
||||
|
@ -47,9 +53,16 @@ class OrderController extends base
|
|||
|
||||
if($os_status) {
|
||||
if ($os_status[0] == array_search(Orders::OSS[4],Orders::OSS)){
|
||||
$query->whereRaw(Orders::AllOssStatusSql[$os_status[1]]);
|
||||
if (isset($os_status[1]) && is_numeric($os_status[1])) {
|
||||
$query->whereRaw(Orders::AllOssStatusSql[$os_status[1]]);
|
||||
}
|
||||
}else{
|
||||
$query->where('os', $os_status[0] ?? 0 )->where('order_status', $os_status[1] ?? 0);
|
||||
if (isset($os_status[0]) && is_numeric($os_status[0])) {
|
||||
$query->where('os', $os_status[0]);
|
||||
}
|
||||
if (isset($os_status[1]) && is_numeric($os_status[1])) {
|
||||
$query->where('order_status', $os_status[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,24 +138,27 @@ class OrderController extends base
|
|||
|
||||
$list = $orders->visible(['admin' => ['username','name','avatar']])->hidden(['check_sn'])->append(['order_status_name','status_name','os_name']);
|
||||
|
||||
$localMobiles = [];
|
||||
foreach ($list as &$value) {
|
||||
$mobileInfo = $value['mobileInfo'] ?? '';
|
||||
if (empty($value['mobileInfo'])) {
|
||||
try {
|
||||
$mobileInfo = (new AliCloudApiService())->getMobileArea($value['mobile']);
|
||||
if (isset($localMobiles[$value['mobile']])) {
|
||||
// 防止重复查询第三方接口
|
||||
$mobileInfo = $localMobiles[$value['mobile']];
|
||||
} else {
|
||||
$mobileInfo = (new ThirdApiService())->getMobileArea($value['mobile']);
|
||||
$localMobiles[$value['mobile']] = $mobileInfo;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$mobileInfo = [];
|
||||
Log::info('查询手机归属地失败:' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
$value['mobileInfo'] = $mobileInfo;
|
||||
// if (!empty($mobileInfo)) {
|
||||
// $value['mobile'] = $value['mobile'] . sprintf('(%s-%s)', $mobileInfo['area'], $mobileInfo['originalIsp']);
|
||||
// }
|
||||
}
|
||||
|
||||
$_oss = [];
|
||||
$_oss[] = [];
|
||||
foreach (Orders::OSS as $key=>$os) {
|
||||
$_oss[$key] = ['label' => $os,'value' => $key];
|
||||
if($key == 1) {
|
||||
|
@ -153,6 +169,12 @@ class OrderController extends base
|
|||
$statss = Orders::DouyinStatus;
|
||||
}elseif ($key == 4) {
|
||||
$statss = Orders::AllOssStatus;
|
||||
}elseif($key ==5) {
|
||||
$statss = Orders::DouyinStatus;
|
||||
}elseif($key ==6) {
|
||||
$statss = Orders::TongchengStatus;
|
||||
}elseif($key ==6) {
|
||||
$statss = Orders::OrderStatus;
|
||||
}
|
||||
|
||||
$_ch = [];
|
||||
|
@ -163,7 +185,7 @@ class OrderController extends base
|
|||
$_oss[$key]['children'] = $_ch;
|
||||
}
|
||||
|
||||
return $this->success($list,null,['timetype'=> Orders::timeType, 'oss' => $_oss]);
|
||||
return $this->success($list,null,['timetype'=> Orders::timeType, 'oss' => array_values($_oss)]);
|
||||
}
|
||||
|
||||
public function all(Request $request) {
|
||||
|
@ -239,9 +261,8 @@ class OrderController extends base
|
|||
if(empty($desc) || empty($status)) return $this->error(2004, '跟进说明需要填写!');
|
||||
if(empty($personnel)) return $this->error(2004, '人员情况需要填写!');
|
||||
if(empty($travel_date)) return $this->error(2004, '出游时间需要填写!');
|
||||
if ($status == 1){
|
||||
if (in_array($status, [0, 1])){
|
||||
if(empty($next_follow)) return $this->error(2004, '下次跟进时间需要填写!');
|
||||
|
||||
}
|
||||
if ($status == 2){
|
||||
$next_follow = '';
|
||||
|
@ -372,6 +393,40 @@ class OrderController extends base
|
|||
return $this->success($backs->append(['order_status_name','os_name']),null, Orders::OSS);
|
||||
}
|
||||
|
||||
// 批量流转出
|
||||
public function backBatch(Request $request) {
|
||||
$sn = $request->post('sn');
|
||||
if (empty($sn) || !is_array($sn)) {
|
||||
return $this->error(2001, '请选择流转的订单');
|
||||
}
|
||||
$toAdminId = $request->post('to_admin_id');
|
||||
if (!$toAdminId) {
|
||||
return $this->error(2001, '请选择流转的客服');
|
||||
}
|
||||
|
||||
// Orders::whereIn('sn', $sn)->update(['admin_id'=> $toAdminId]);
|
||||
|
||||
foreach ($sn as $value) {
|
||||
$item = Orders::where('sn', $value)->find();
|
||||
if($item->admin_id == $toAdminId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Backs::create([
|
||||
'order_id' => $item->id,
|
||||
'admin_id' => $toAdminId,
|
||||
'admin' => $item->admin_id,
|
||||
'apply_id' => $request->admin->id,
|
||||
'status' => 1
|
||||
]);
|
||||
Orders::where('sn', $value)->update(['admin_id'=> $toAdminId]);
|
||||
// 发送短信
|
||||
\app\server\Orders::sendOrderSms($toAdminId, $item);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
//把订单拉回
|
||||
public function back(Request $request) {
|
||||
$sn = $request->post('sn');
|
||||
|
@ -568,6 +623,42 @@ class OrderController extends base
|
|||
}
|
||||
}
|
||||
|
||||
return $this->success(implode("\n", $flow));
|
||||
}catch(\Exception $e) {
|
||||
Log::info(sprintf('oneClickRepair error:%s, %s, %s', $e->getMessage(), $e->getFile(), $e->getLine()) );
|
||||
return $this->error(2006, '出错了:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预约处理
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function changeAppointmentStatus(Request $request)
|
||||
{
|
||||
try {
|
||||
$ids = $request->post('id', 0);
|
||||
$idArr = explode(',', $ids);
|
||||
|
||||
$flow = [];
|
||||
foreach ($idArr as $id) {
|
||||
Log::info('同步订单ID:' . $id);
|
||||
|
||||
$order = Orders::where('id', $id)->find();
|
||||
|
||||
if (empty($order)) {
|
||||
return $this->error(2004, '记录没有找到.');
|
||||
}
|
||||
|
||||
// 订单是待使用状态,先同步第三方状态
|
||||
$order['appointment_status'] = 2;
|
||||
$order->save();
|
||||
}
|
||||
|
||||
return $this->success(implode("\n", $flow));
|
||||
}catch(\Exception $e) {
|
||||
return $this->error(2006, '出错了:' . $e->getMessage());
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\model\Admins;
|
||||
use app\model\Onlines;
|
||||
use app\model\Orders;
|
||||
use app\model\Products;
|
||||
use support\Log;
|
||||
use support\Request;
|
||||
use support\Redis;
|
||||
|
||||
class ProductsController extends base {
|
||||
/**
|
||||
* 线路列表
|
||||
* @param Request $request
|
||||
* @return \support\Response
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function list(Request $request) {
|
||||
$query = Products::where('status', 1)->order('id', 'desc');
|
||||
if($username = $request->get('username')) {
|
||||
$query->where('username', $username);
|
||||
}
|
||||
if($status = $request->get('status')) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
if($is_order = $request->get('is_order')) {
|
||||
$query->where('is_order', $is_order);
|
||||
}
|
||||
|
||||
$list = $query->paginate($request->get('limit',1000));
|
||||
return $this->success($list,null,['oss' => array_values(array_map(function ($os, $k) {
|
||||
return ['id' => $k, 'os' => $os];
|
||||
}, Orders::OSS, array_keys(Orders::OSS)))]);
|
||||
}
|
||||
|
||||
public function add(Request $request) {
|
||||
if (!$request->post('os')) {
|
||||
return $this->error(2001, '请选择平台.');
|
||||
}
|
||||
if (!$request->post('product_name')) {
|
||||
return $this->error(2001, '线路名称必填');
|
||||
}
|
||||
if (!$request->post('third_product_id')) {
|
||||
return $this->error(2001, '线路id必填');
|
||||
}
|
||||
|
||||
$where = ['os' => $request->post('os'), 'third_product_id' => $request->post('third_product_id')];
|
||||
$product = (new Products())->where($where)->find();
|
||||
if (!empty($product)) {
|
||||
return $this->error(2002, '线路已存在');
|
||||
}
|
||||
|
||||
$product = new Products();
|
||||
$product->os = $request->post('os');
|
||||
$product->third_product_id = $request->post('third_product_id');
|
||||
$product->product_name = $request->post('product_name');
|
||||
$product->status = 1;
|
||||
$product->save();
|
||||
Log::info('product:' . json_encode($product));
|
||||
return $this->success([]);
|
||||
}
|
||||
}
|
|
@ -38,6 +38,18 @@ class QaController extends base
|
|||
}])
|
||||
->paginate($limit);
|
||||
|
||||
$list = json_decode(json_encode($list), true);
|
||||
if (isset($list['data']) && is_array($list['data'])) {
|
||||
foreach ($list['data'] as &$item) {
|
||||
if (isset($item['img_zip']) && !empty($item['img_zip'])) {
|
||||
$item['img_zip'] = json_decode($item['img_zip'], true);
|
||||
}
|
||||
if (isset($item['trip_zip']) && !empty($item['trip_zip'])) {
|
||||
$item['trip_zip'] = json_decode($item['trip_zip'], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success($list);
|
||||
}
|
||||
|
||||
|
@ -65,8 +77,12 @@ class QaController extends base
|
|||
$list = $list->paginate($request->get('limit',10))->toArray();
|
||||
|
||||
foreach ($list['data'] as &$item) {
|
||||
$item['img_zip'] = $item['img_zip'] ? explode(',', $item['img_zip']) : [];
|
||||
$item['trip_zip'] = $item['trip_zip'] ? explode(',', $item['trip_zip']) : [];
|
||||
if (isset($item['img_zip']) && !empty($item['img_zip'])) {
|
||||
$item['img_zip'] = json_decode($item['img_zip'], true);
|
||||
}
|
||||
if (isset($item['trip_zip']) && !empty($item['trip_zip'])) {
|
||||
$item['trip_zip'] = json_decode($item['trip_zip'], true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success($list);
|
||||
|
@ -90,15 +106,22 @@ class QaController extends base
|
|||
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 ($post['trip_zip']) {
|
||||
$post['trip_zip'] = implode(',', $post['trip_zip']);
|
||||
if ($post['trip_zip'] && is_array($post['trip_zip'])) {
|
||||
$post['trip_zip'] = json_encode($post['trip_zip']);
|
||||
}
|
||||
if ($post['img_zip']) {
|
||||
$post['img_zip'] = implode(',', $post['img_zip']);
|
||||
if ($post['img_zip'] && is_array($post['img_zip'])) {
|
||||
$post['img_zip'] = json_encode($post['img_zip']);
|
||||
}
|
||||
|
||||
$qaQuestion = $post['qaQuestions'] ?? null;
|
||||
unset($post['qaQuestions']);
|
||||
if (isset($post['update_time'])) {
|
||||
unset($post['update_time']);
|
||||
}
|
||||
if (isset($post['id'])) {
|
||||
unset($post['id']);
|
||||
}
|
||||
$post['create_time'] = time();
|
||||
|
||||
try {
|
||||
Db::transaction(function () use ($post,$qaQuestion){
|
||||
|
@ -130,11 +153,11 @@ class QaController extends base
|
|||
unset($post['update_time']);
|
||||
unset($post['qaQuestions']);
|
||||
|
||||
if ($post['trip_zip']) {
|
||||
$post['trip_zip'] = implode(',', $post['trip_zip']);
|
||||
if (isset($post['trip_zip']) && $post['trip_zip']) {
|
||||
$post['trip_zip'] = json_encode($post['trip_zip']);
|
||||
}
|
||||
if ($post['img_zip']) {
|
||||
$post['img_zip'] = implode(',', $post['img_zip']);
|
||||
if (isset($post['img_zip']) && $post['img_zip']) {
|
||||
$post['img_zip'] = json_encode($post['img_zip']);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use app\model\Admins;
|
||||
use app\model\LiveRoomWorks;
|
||||
use app\model\Orders;
|
||||
use app\model\Sales;
|
||||
use app\model\Works;
|
||||
use Carbon\Carbon;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use think\facade\Db;
|
||||
|
||||
class LiveRoomWorkCommand extends Command
|
||||
{
|
||||
|
||||
protected static $defaultName = 'LiveRoomWorkCommand';
|
||||
protected static $defaultDescription = '直播间排班表处理';
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('LiveRoomWorkCommand')
|
||||
->setDescription('直播间排班表处理')
|
||||
->setDefinition(
|
||||
new InputDefinition(array(
|
||||
new InputOption('room_id', 'd', InputOption::VALUE_REQUIRED),
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('LiveRoomWorkService start');
|
||||
// 找出直播已结束的排班
|
||||
$now = Carbon::now()->toDateTimeString();
|
||||
$endRooms = LiveRoomWorks::where([['end', '<', $now]])->whereNull('orders')->select();
|
||||
|
||||
// 统计排班数据
|
||||
foreach ($endRooms as $endRoom) {
|
||||
$orderSum = Orders::where('live_room_work_id', $endRoom->id)
|
||||
->fieldRaw('count(1) as order_num')
|
||||
->fieldRaw('sum(total_price) as total_price')
|
||||
->find();
|
||||
LiveRoomWorks::where('id', $endRoom->id)->update(['orders' => $orderSum->order_num ?? 0, 'total' => $orderSum->total_price ?? 0]);
|
||||
}
|
||||
|
||||
$output->writeln('LiveRoomWorkService end');
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace app\command;
|
||||
|
||||
use app\admin\controller\AdminController;
|
||||
use app\common\Error;
|
||||
use app\model\Admins;
|
||||
use app\model\Blacks;
|
||||
use app\model\FilterMobiles;
|
||||
use app\model\Finances;
|
||||
use app\model\Logs;
|
||||
use app\model\Orders;
|
||||
|
@ -41,10 +44,10 @@ class SpiderDy extends Command
|
|||
);
|
||||
}
|
||||
|
||||
protected function users()
|
||||
protected function users($routeType)
|
||||
{
|
||||
if (count($this->_users) > 0) return $this->_users;
|
||||
$users = Admins::where('status', 1)->where('is_order', 1)->select();
|
||||
// if (count($this->_users) > 0) return $this->_users;
|
||||
$users = Admins::where('status', 1)->where('is_order', 1)->where('route_type', $routeType)->select();
|
||||
$us = [];
|
||||
foreach ($users as $u) {
|
||||
$ru = Redis::get('CRM:USER:ONLINE:' . $u->id);
|
||||
|
@ -60,11 +63,21 @@ class SpiderDy extends Command
|
|||
|
||||
protected $_redis_pool = [];
|
||||
|
||||
protected function poolUser($status = 0)
|
||||
protected function poolUser($status = 0, $categoryDesc = '')
|
||||
{
|
||||
$routeType = 10;
|
||||
// 抖音境内外类型判断
|
||||
$abroadKeyWords = ['曼谷', '港', '澳', '泰国', '普吉岛', '境外'];
|
||||
foreach ($abroadKeyWords as $kw) {
|
||||
if (mb_strpos($categoryDesc, $kw) !== false) {
|
||||
$routeType = 20;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$status .= $routeType;
|
||||
if (empty($this->_redis_pool[$status])) {
|
||||
$this->_redis_pool[$status] = Redis::hGetAll('CRM:Pool:' . $status);
|
||||
$users = $this->users();
|
||||
$users = $this->users($routeType);
|
||||
$_users = [];
|
||||
if (empty($this->_redis_pool[$status])) {
|
||||
foreach ($users as $user) {
|
||||
|
@ -137,18 +150,6 @@ class SpiderDy extends Command
|
|||
return 1;
|
||||
}
|
||||
|
||||
// echo "状态1\n";
|
||||
// $this->poolUser(1);
|
||||
// echo "状态2\n";
|
||||
// $this->poolUser(2);
|
||||
// echo "状态3\n";
|
||||
// $this->poolUser(3);
|
||||
// echo "状态4\n";
|
||||
// $this->poolUser(4);
|
||||
// echo "状态5\n";
|
||||
// $this->poolUser(5);
|
||||
// return 1;
|
||||
|
||||
// $this->checks($output);
|
||||
// $output->writeln('CHECK spider:dy');
|
||||
|
||||
|
@ -166,9 +167,14 @@ class SpiderDy extends Command
|
|||
// // $this->_kuaishouOrder($start, $end, false);
|
||||
// }
|
||||
|
||||
$start = date('Y-m-d 00:00:00', strtotime("-{$d} days"));
|
||||
$end = date('Y-m-d 23:59:59');
|
||||
$this->orders($start, $end, false);
|
||||
// 8点20 开始分配订单
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
$startTime = strtotime(date('Y-m-d 09:10'));
|
||||
if (time() > $startTime) {
|
||||
$start = date('Y-m-d 00:00:00', strtotime("-{$d} days"));
|
||||
$end = date('Y-m-d 23:59:59');
|
||||
$this->orders($start, $end, false);
|
||||
}
|
||||
|
||||
if (date('H') >= 1 && date('H') <= 7) {
|
||||
$this->reload($output);
|
||||
|
@ -228,19 +234,15 @@ class SpiderDy extends Command
|
|||
$order->is_check = 1;
|
||||
$order->check_sn = $json->check_sn;
|
||||
$back = $order->save();
|
||||
echo "订单在自己名下";
|
||||
} else {
|
||||
// echo "订单在自己名下2 $order_id";
|
||||
$order = Orders::where('sn', $order_id)->find();
|
||||
// print_r($order->toArray());
|
||||
if (!empty($order) && $order->admin_id == 0) {
|
||||
echo '订单不在自己的名下,并且没有分人。';
|
||||
$order->is_check = 1;
|
||||
$order->check_sn = $json->check_sn;
|
||||
$order->admin_id = $json->admin_id;
|
||||
$back = $order->save();
|
||||
} elseif (!empty($order) && $order->admin_id !== $json->admin_id) {
|
||||
echo '订单不在自己的名下,并且分人了。';
|
||||
$item = new Orders();
|
||||
$item->os = 1;
|
||||
$item->sn = $order->sn;
|
||||
|
@ -299,7 +301,7 @@ class SpiderDy extends Command
|
|||
{
|
||||
|
||||
foreach (Orders::OSS as $k => $os) {
|
||||
$pages = 6;
|
||||
$pages = 3;
|
||||
$page = 1;
|
||||
|
||||
while (true) {
|
||||
|
@ -328,10 +330,10 @@ class SpiderDy extends Command
|
|||
break;*/
|
||||
|
||||
case 3:
|
||||
// echo '==========pages='.$pages;
|
||||
case 5:
|
||||
try {
|
||||
$dou = new Douyin();
|
||||
$list = $dou->get($page, $start, $end);
|
||||
$dou = new Douyin($k);
|
||||
$list = $dou->get($page, $start, $end, '');
|
||||
$pages = $dou->totalPage;
|
||||
} catch (\Exception $e) {
|
||||
Log::error(dirname(__FILE__) . __LINE__ . $e);
|
||||
|
@ -344,31 +346,50 @@ class SpiderDy extends Command
|
|||
}
|
||||
|
||||
foreach ($list as $order) {
|
||||
// 过滤一日游
|
||||
if ($order->os == 3 && strpos($order->category_desc, '一日游') !== false) {
|
||||
Log::info("抖音 跳过订单:{$order->order_id}");
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = Orders::where('os', $order->os)->where('sn', $order->sn)->find();
|
||||
|
||||
if (empty($item)) {
|
||||
$order->is_zhibo = $this->zhibo($order->product_name, $order->os);
|
||||
|
||||
$oldMobile = orders::where("os", $order->os)->where("mobile", $order->mobile)
|
||||
->where("create_time", '>=', time() - 24 * 3600 * 3)->find();
|
||||
|
||||
if (!empty($oldMobile)) {
|
||||
$admin_id = $oldMobile->admin_id;
|
||||
if (FilterMobiles::isFilterMobile($order->mobile)) {
|
||||
$admin_id = 0;
|
||||
} else {
|
||||
$admin_id = $this->poolUser($order->orderStatus);
|
||||
$oldMobile = orders::where("os", $order->os)->where("mobile", $order->mobile)
|
||||
->where("create_time", '>=', time() - 24 * 3600 * 3)->find();
|
||||
|
||||
if (!empty($oldMobile)) {
|
||||
$admin_id = $oldMobile->admin_id;
|
||||
} else {
|
||||
// $admin_id = $this->poolUser($order->orderStatus, $order->category_desc);
|
||||
try {
|
||||
$admin_id = \app\server\Orders::poolUser($order->orderStatus, $order->product_id);
|
||||
} catch (\Exception $exception) {
|
||||
Log::info(sprintf('dy create order fail:%s, order_id:%s', $exception->getMessage(), $order->sn));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($admin_id)) return null;
|
||||
}
|
||||
|
||||
if (empty($admin_id)) return null;
|
||||
$order->admin_id = $admin_id;
|
||||
$order->give_time = time();
|
||||
|
||||
//判断是否需要发短信
|
||||
if (($order->os == 1 && array_key_exists($order->order_status, [1 => 1, 3 => 3])) ||
|
||||
($order->os == 2 && array_key_exists($order->order_status, [2 => 2, 3 => 3, 4 => 4])) ||
|
||||
($order->os == 3 && $order->order_status == 1)) {
|
||||
// $this->sms($admin_id, $order);
|
||||
if ($order->order_status == 1) {
|
||||
$this->sms($admin_id, $order);
|
||||
}
|
||||
|
||||
$work = \app\server\Orders::getLiveRoomWork($order->create_at, $order->product_id);
|
||||
if ($work) {
|
||||
$order->live_room_work_id = $work->id;
|
||||
}
|
||||
|
||||
//新获得一个用户的,提示管理员有新的订单
|
||||
Redis::incrBy('CRM:USER:ONLINE:NEW:' . $admin_id, 1);
|
||||
$item = new Orders();
|
||||
|
@ -381,6 +402,10 @@ class SpiderDy extends Command
|
|||
}
|
||||
}
|
||||
|
||||
if ($item->order_status !== 2 && $order->order_status == 2) {
|
||||
Redis::incrBy('CRM:USER:WRITE:OFF:' . $item->admin_id, 1);
|
||||
}
|
||||
|
||||
$back = $item->save($order->toArray());
|
||||
|
||||
if ($back) {
|
||||
|
@ -388,6 +413,10 @@ class SpiderDy extends Command
|
|||
}
|
||||
}
|
||||
|
||||
// 数据小于50, 结束
|
||||
if (empty($list)) {
|
||||
break;
|
||||
}
|
||||
$page++;
|
||||
}
|
||||
}
|
||||
|
@ -397,7 +426,7 @@ class SpiderDy extends Command
|
|||
{
|
||||
$back = Redis::set('SpiderMt:reload:dy:lock', time(), 'EX', 3600 * 8, 'NX');
|
||||
if (!$back) return;
|
||||
$orders = Orders::where('create_at', '<=', (time() - 15 * 24 * 3600) * 1000)->where('os', 3)->wherein('status', [1, 2, 3])->select();
|
||||
$orders = Orders::where('create_at', '<=', (time() - 15 * 24 * 3600) * 1000)->whereIn('os', [3, 5])->wherein('status', [1, 2, 3])->select();
|
||||
foreach ($orders as $order) {
|
||||
$this->reloadStatus($order->sn, $order->os, $output);
|
||||
}
|
||||
|
@ -422,7 +451,8 @@ class SpiderDy extends Command
|
|||
$it = $m->get(1, null, null, $item->sn);
|
||||
break;*/
|
||||
case 3:
|
||||
$m = new Douyin();
|
||||
case 5:
|
||||
$m = new Douyin($item->os);
|
||||
$it = $m->get(1, null, null, $item->sn);
|
||||
break;
|
||||
|
||||
|
@ -458,18 +488,14 @@ class SpiderDy extends Command
|
|||
{
|
||||
$user = Admins::cache(true)->where('id', $admin_id)->find();
|
||||
if ((!config('app.debug', true) || config('app.debug', true) === 'false') && (time() * 1000 - $order->create_at) / 1000 < 2 * 24 * 3600) {
|
||||
echo "发送短信\n";
|
||||
print_r([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
|
||||
$has = Blacks::where('mobile', $order->mobile)->find();
|
||||
if (empty($has) && !empty($order->mobile)) {
|
||||
SMS::juhe_sms_send($order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]);
|
||||
} else {
|
||||
echo "黑名单不发送短信\n";
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
} else {
|
||||
echo "不发送短信\n";
|
||||
print_r([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace app\command;
|
||||
|
||||
use app\admin\controller\AdminController;
|
||||
use app\model\Admins;
|
||||
use app\model\Blacks;
|
||||
use app\model\FilterMobiles;
|
||||
use app\model\Finances;
|
||||
use app\model\Logs;
|
||||
use app\model\Orders;
|
||||
|
@ -42,10 +44,10 @@ class SpiderMt extends Command
|
|||
);
|
||||
}
|
||||
|
||||
protected function users()
|
||||
protected function users($routeType)
|
||||
{
|
||||
if (count($this->_users) > 0) return $this->_users;
|
||||
$users = Admins::where('status', 1)->where('is_order', 1)->select();
|
||||
// if (count($this->_users) > 0) return $this->_users;
|
||||
$users = Admins::where('status', 1)->where('is_order', 1)->where('route_type', $routeType)->select();
|
||||
$us = [];
|
||||
foreach ($users as $u) {
|
||||
$ru = Redis::get('CRM:USER:ONLINE:' . $u->id);
|
||||
|
@ -84,11 +86,13 @@ class SpiderMt extends Command
|
|||
|
||||
protected $_redis_pool = [];
|
||||
|
||||
protected function poolUser($status = 0)
|
||||
protected function poolUser($status = 0, $categoryDesc = '')
|
||||
{
|
||||
$routeType = AdminController::ROUTE_LISTS[mb_substr($categoryDesc, 0, 2)] ?? 10;
|
||||
$status .= $routeType;
|
||||
if (empty($this->_redis_pool[$status])) {
|
||||
$this->_redis_pool[$status] = Redis::hGetAll('CRM:Pool:' . $status);
|
||||
$users = $this->users();
|
||||
$users = $this->users($routeType);
|
||||
$_users = [];
|
||||
if (empty($this->_redis_pool[$status])) {
|
||||
foreach ($users as $user) {
|
||||
|
@ -134,7 +138,7 @@ class SpiderMt extends Command
|
|||
Redis::hIncrBy('CRM:Pool:' . $status, $username, 1);
|
||||
} catch (\Exception $e) {
|
||||
Log::error(dirname(__FILE__) . __LINE__ . '没有可以分配的用户', (array)json_encode($e));
|
||||
throw new \Exception('没有可以分配的用户');
|
||||
throw new \Exception('没有可以分配的用户' . $status . '--' . $categoryDesc);
|
||||
}
|
||||
return $username;
|
||||
}
|
||||
|
@ -158,21 +162,8 @@ class SpiderMt extends Command
|
|||
|
||||
if ($orderid) {
|
||||
$this->reloadStatus($orderid, $os, $output);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// echo "状态1\n";
|
||||
// $this->poolUser(1);
|
||||
// echo "状态2\n";
|
||||
// $this->poolUser(2);
|
||||
// echo "状态3\n";
|
||||
// $this->poolUser(3);
|
||||
// echo "状态4\n";
|
||||
// $this->poolUser(4);
|
||||
// echo "状态5\n";
|
||||
// $this->poolUser(5);
|
||||
// return 1;
|
||||
|
||||
// $this->checks($output);
|
||||
// $output->writeln('CHECK spider:mt');
|
||||
|
||||
|
@ -191,9 +182,13 @@ class SpiderMt extends Command
|
|||
// // $this->_kuaishouOrder($start, $end, false);
|
||||
// }
|
||||
|
||||
$start = date('Y-m-d 00:00:00', strtotime("-{$d} days"));
|
||||
$end = date('Y-m-d 23:59:59');
|
||||
$this->orders($start, $end, false);
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
$startTime = strtotime(date('Y-m-d 09:10'));
|
||||
if (time() > $startTime) {
|
||||
$start = date('Y-m-d 00:00:00', strtotime("-{$d} days"));
|
||||
$end = date('Y-m-d 23:59:59');
|
||||
$this->orders($start, $end, $orderid);
|
||||
}
|
||||
|
||||
if (date('H') >= 1 && date('H') <= 7) {
|
||||
$this->reload($output);
|
||||
|
@ -253,20 +248,16 @@ class SpiderMt extends Command
|
|||
$order->is_check = 1;
|
||||
$order->check_sn = $json->check_sn;
|
||||
$back = $order->save();
|
||||
echo "订单在自己名下";
|
||||
} else {
|
||||
// echo "订单在自己名下2 $order_id";
|
||||
$order = Orders::where('sn', $order_id)->find();
|
||||
// print_r($order->toArray());
|
||||
if (!empty($order) && $order->admin_id == 0) {
|
||||
echo '订单不在自己的名下,并且没有分人。';
|
||||
$order->is_check = 1;
|
||||
$order->check_sn = $json->check_sn;
|
||||
$order->admin_id = $json->admin_id;
|
||||
$order->give_time = time();
|
||||
$back = $order->save();
|
||||
} elseif (!empty($order) && $order->admin_id !== $json->admin_id) {
|
||||
echo '订单不在自己的名下,并且分人了。';
|
||||
$item = new Orders();
|
||||
$item->os = 1;
|
||||
$item->sn = $order->sn;
|
||||
|
@ -326,7 +317,7 @@ class SpiderMt extends Command
|
|||
{
|
||||
|
||||
foreach (Orders::OSS as $k => $os) {
|
||||
$pages = 6;
|
||||
$pages = 20;
|
||||
$page = 1;
|
||||
|
||||
while (true) {
|
||||
|
@ -335,15 +326,11 @@ class SpiderMt extends Command
|
|||
|
||||
switch ($k) {
|
||||
case 1:
|
||||
case 7:
|
||||
try {
|
||||
$mei = new Meituan();
|
||||
$list = $mei->get($page, $start, $end);
|
||||
$mei = new Meituan($k);
|
||||
$list = $mei->get($page, $start, $end, $order_id);
|
||||
// $pages = $mei->totalPage;
|
||||
|
||||
if ($mei->totalPage === 1) {
|
||||
$pages = 1;
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(dirname(__FILE__) . __LINE__ . $e);
|
||||
}
|
||||
|
@ -378,27 +365,42 @@ class SpiderMt extends Command
|
|||
|
||||
$item = Orders::where('os', $order->os)->where('sn', $order->sn)->find();
|
||||
if (empty($item)) {
|
||||
$order->is_zhibo = $this->zhibo($order->product_name, $order->os);
|
||||
//$order->is_zhibo = $this->zhibo($order->product_name, $order->os);
|
||||
|
||||
$oldMobile = orders::where("os", $order->os)->where("mobile", $order->mobile)
|
||||
->where("create_time", '>=', time() - 24 * 3600 * 3)->find();
|
||||
|
||||
if (!empty($oldMobile)) {
|
||||
$admin_id = $oldMobile->admin_id;
|
||||
// 刷单账号
|
||||
if (FilterMobiles::isFilterMobile($order->mobile)) {
|
||||
$admin_id = 0;
|
||||
} else {
|
||||
$admin_id = $this->poolUser($order->orderStatus);
|
||||
}
|
||||
$oldMobile = orders::where("os", $order->os)->where("mobile", $order->mobile)
|
||||
->where("create_time", '>=', time() - 24 * 3600 * 3)->find();
|
||||
|
||||
if (empty($admin_id)) return null;
|
||||
if (!empty($oldMobile)) {
|
||||
$admin_id = $oldMobile->admin_id;
|
||||
} else {
|
||||
try {
|
||||
// $admin_id = $this->poolUser($order->orderStatus, $order->category_desc);
|
||||
$admin_id = \app\server\Orders::poolUser($order->orderStatus, $order->product_id);
|
||||
} catch (\Exception $exception) {
|
||||
Log::info(sprintf('mt create order fail:%s, order_id:%s', $exception->getMessage(), $order->sn));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (empty($admin_id)) return null;
|
||||
}
|
||||
$order->admin_id = $admin_id;
|
||||
$order->give_time = time();
|
||||
|
||||
//判断是否需要发短信
|
||||
if (($order->os == 1 && array_key_exists($order->order_status, [1 => 1, 3 => 3])) ||
|
||||
($order->os == 2 && array_key_exists($order->order_status, [2 => 2, 3 => 3, 4 => 4])) ||
|
||||
($order->os == 3 && $order->order_status == 1)) {
|
||||
$this->sms($admin_id, $order);
|
||||
if ((in_array($order->os, [1, 7]) && array_key_exists($order->order_status, [1 => 1, 3 => 3]))) {
|
||||
if ($admin_id > 0) {
|
||||
$this->sms($admin_id, $order);
|
||||
}
|
||||
}
|
||||
$work = \app\server\Orders::getLiveRoomWork($order->create_at, $order->product_id);
|
||||
if ($work) {
|
||||
$order->live_room_work_id = $work->id;
|
||||
}
|
||||
Log::info(sprintf('mt create order success order_id:%s', $order->sn));
|
||||
//新获得一个用户的,提示管理员有新的订单
|
||||
Redis::incrBy('CRM:USER:ONLINE:NEW:' . $admin_id, 1);
|
||||
$item = new Orders();
|
||||
|
@ -411,6 +413,10 @@ class SpiderMt extends Command
|
|||
}
|
||||
}
|
||||
|
||||
if ($item->order_status !== 4 && $order->order_status == 4) {
|
||||
Redis::incrBy('CRM:USER:WRITE:OFF:' . $item->admin_id, 1);
|
||||
}
|
||||
|
||||
$back = $item->save($order->toArray());
|
||||
|
||||
if ($back) {
|
||||
|
@ -418,6 +424,10 @@ class SpiderMt extends Command
|
|||
}
|
||||
}
|
||||
|
||||
// 数据小于50, 结束
|
||||
if (empty($list)) {
|
||||
break;
|
||||
}
|
||||
$page++;
|
||||
}
|
||||
}
|
||||
|
@ -440,11 +450,13 @@ class SpiderMt extends Command
|
|||
$item = Orders::where($w)->find();
|
||||
if (empty($item)) {
|
||||
$output->writeln('没有找到订单');
|
||||
return;
|
||||
}
|
||||
$it = null;
|
||||
switch ($item->os) {
|
||||
case 1:
|
||||
$m = new Meituan();
|
||||
case 7:
|
||||
$m = new Meituan($item->os);
|
||||
$it = $m->get(1, null, null, $item->sn);
|
||||
break;
|
||||
/* case 2:
|
||||
|
@ -488,19 +500,16 @@ class SpiderMt extends Command
|
|||
{
|
||||
$user = Admins::cache(true)->where('id', $admin_id)->find();
|
||||
if ((!config('app.debug', true) || config('app.debug', true) === 'false') && (time() * 1000 - $order->create_at) / 1000 < 2 * 24 * 3600) {
|
||||
echo "发送短信\n";
|
||||
print_r([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
|
||||
$has = Blacks::where('mobile', $order->mobile)->find();
|
||||
if (empty($has) && !empty($order->mobile)) {
|
||||
SMS::juhe_sms_send($order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]);
|
||||
} else {
|
||||
echo "黑名单不发送短信\n";
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
} else {
|
||||
echo "不发送短信\n";
|
||||
print_r([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
|
||||
Log::info('不发送短信' . json_encode([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,408 @@
|
|||
<?php
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use app\admin\controller\AdminController;
|
||||
use app\model\Admins;
|
||||
use app\model\Blacks;
|
||||
use app\model\Finances;
|
||||
use app\model\Orders;
|
||||
use app\server\SMS;
|
||||
use app\server\Tongcheng;
|
||||
use stdClass;
|
||||
use support\Log;
|
||||
use support\Redis;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* 同程数据拉取
|
||||
*/
|
||||
class SpiderTc extends Command
|
||||
{
|
||||
protected static $defaultName = 'spider:tc';
|
||||
protected static $defaultDescription = '同程订单拉取器';
|
||||
|
||||
protected $_users = [];
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('spider:tc')
|
||||
->setDescription('同程订单拉取器')
|
||||
->setDefinition(
|
||||
new InputDefinition(array(
|
||||
new InputOption('sn', 'sn', InputOption::VALUE_REQUIRED),
|
||||
new InputOption('os', 'os', InputOption::VALUE_REQUIRED),
|
||||
new InputOption('day', 'day', InputOption::VALUE_REQUIRED)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
protected function users($routeType)
|
||||
{
|
||||
// if (count($this->_users) > 0) return $this->_users;
|
||||
$users = Admins::where('status', 1)->where('is_order', 1)->where('route_type', $routeType)->select();
|
||||
$us = [];
|
||||
foreach ($users as $u) {
|
||||
$ru = Redis::get('CRM:USER:ONLINE:' . $u->id);
|
||||
if (empty($ru)) continue;
|
||||
|
||||
$_u = new stdClass();
|
||||
$_u->username = $u->id;
|
||||
$us[] = $_u;
|
||||
};
|
||||
$this->_users = $us;
|
||||
return $this->_users;
|
||||
}
|
||||
|
||||
protected $_redis_pool = [];
|
||||
|
||||
protected function poolUser($status = 0, $categoryDesc = '')
|
||||
{
|
||||
$routeType = AdminController::ROUTE_LISTS[mb_substr($categoryDesc, 0, 2)] ?? 10;
|
||||
$status .= $routeType;
|
||||
if (empty($this->_redis_pool[$status])) {
|
||||
$this->_redis_pool[$status] = Redis::hGetAll('CRM:Pool:' . $status);
|
||||
$users = $this->users($routeType);
|
||||
$_users = [];
|
||||
if (empty($this->_redis_pool[$status])) {
|
||||
foreach ($users as $user) {
|
||||
$_users[$user->username] = 0;
|
||||
Redis::hSet('CRM:Pool:' . $status, $user->username, 0);
|
||||
}
|
||||
$this->_redis_pool[$status] = $_users;
|
||||
} else {
|
||||
asort($this->_redis_pool[$status]);
|
||||
$key_users = array_keys($this->_redis_pool[$status]);
|
||||
$username = $key_users[0];
|
||||
$max = $this->_redis_pool[$status][$username];
|
||||
$_users = [];
|
||||
foreach ($users as $user) {
|
||||
$_users[] = $user->username;
|
||||
if (!in_array($user->username, $key_users)) {
|
||||
$this->_redis_pool[$status][$username] = $max;
|
||||
Redis::hSet('CRM:Pool:' . $status, $user->username, $max);
|
||||
}
|
||||
}
|
||||
foreach ($this->_redis_pool[$status] as $username => $val) {
|
||||
if (!in_array($username, $_users)) {
|
||||
unset($this->_redis_pool[$status][$username]);
|
||||
Redis::hDel('CRM:Pool:' . $status, $username);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$username = null;
|
||||
try {
|
||||
$pool = $this->_redis_pool[$status];
|
||||
if (empty($pool)) $pool = [];
|
||||
asort($pool);
|
||||
$keys = array_keys($pool);
|
||||
// if(!!config('app.debug', true)) return 1;
|
||||
if (empty($keys)) {
|
||||
Log::error(dirname(__FILE__) . __LINE__ . '没有可以分配的用户');
|
||||
throw new \Exception('没有可以分配的用户');
|
||||
}
|
||||
$username = $keys[0];
|
||||
$this->_redis_pool[$status][$username] += 1;
|
||||
Redis::hIncrBy('CRM:Pool:' . $status, $username, 1);
|
||||
} catch (\Exception $e) {
|
||||
Log::error(dirname(__FILE__) . __LINE__ . '没有可以分配的用户', (array)json_encode($e));
|
||||
throw new \Exception('没有可以分配的用户');
|
||||
}
|
||||
return $username;
|
||||
}
|
||||
|
||||
/** @var InputInterface $output */
|
||||
protected $output;
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
// $name = $input->getArgument('name');
|
||||
$this->output = $output;
|
||||
$output->writeln('START spider:tc:'.date('Y-m-d H:i:s'));
|
||||
|
||||
$os = $input->getOption('os');
|
||||
$orderid = $input->getOption('sn');
|
||||
$d = $input->getOption('day');
|
||||
if (empty($d)) {
|
||||
$d = 40;
|
||||
}
|
||||
|
||||
if ($orderid) {
|
||||
$this->reloadStatus($orderid, $os, $output);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// echo "状态1\n";
|
||||
// $this->poolUser(1);
|
||||
// echo "状态2\n";
|
||||
// $this->poolUser(2);
|
||||
// echo "状态3\n";
|
||||
// $this->poolUser(3);
|
||||
// echo "状态4\n";
|
||||
// $this->poolUser(4);
|
||||
// echo "状态5\n";
|
||||
// $this->poolUser(5);
|
||||
// return 1;
|
||||
|
||||
// $this->checks($output);
|
||||
// $output->writeln('CHECK spider:dy');
|
||||
|
||||
// $day = new Douyin();
|
||||
// print_r($day->get(1));
|
||||
// return 1;
|
||||
|
||||
// sleep(5);
|
||||
// $time = strtotime(date('Y-m-d'));
|
||||
// for ($i = 0; $i <= $d; $i++) {
|
||||
// $day = $time - $i * 24 * 3600;
|
||||
// $start = date('Y-m-d', $day);
|
||||
// $end = date('Y-m-d 23:59:59', $day);
|
||||
// $this->orders($start, $end, false);
|
||||
// // $this->_kuaishouOrder($start, $end, false);
|
||||
// }
|
||||
|
||||
// 8点20 开始分配订单
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
$startTime = strtotime(date('Y-m-d 09:10'));
|
||||
if (time() > $startTime) {
|
||||
$start = date('Y-m-d 00:00:00', strtotime("-{$d} days"));
|
||||
$output->writeln('END spider:tc:start:'.$start);
|
||||
$end = date('Y-m-d 23:59:59');
|
||||
$this->orders($start, $end, false);
|
||||
}
|
||||
|
||||
if (date('H') >= 1 && date('H') <= 7) {
|
||||
$this->reload($output);
|
||||
}
|
||||
|
||||
$output->writeln('END spider:tc:'.date('Y-m-d H:i:s'));
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function orders($start = null, $end = null, $order_id = false)
|
||||
{
|
||||
foreach (Orders::OSS as $k => $os) {
|
||||
$pages = 10;
|
||||
$page = 1;
|
||||
while (true) {
|
||||
if ($page > $pages) break;
|
||||
$list = [];
|
||||
|
||||
switch ($k) {
|
||||
case 6:
|
||||
try {
|
||||
$dou = new Tongcheng($k);
|
||||
$list = $dou->get($page, $start, $end, '');
|
||||
$pages = $dou->totalPage;
|
||||
} catch (\Exception $e) {
|
||||
Log::info('同程订单拉取失败:' . dirname(__FILE__) . __LINE__ . $e);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($list as $order) {
|
||||
$item = Orders::where('os', $order->os)->where('sn', $order->sn)->find();
|
||||
|
||||
if (empty($item)) {
|
||||
$order->is_zhibo = 2;
|
||||
|
||||
$oldMobile = orders::where("os", $order->os)->where("mobile", $order->mobile)
|
||||
->where("create_time", '>=', time() - 24 * 3600 * 3)->find();
|
||||
|
||||
if (!empty($oldMobile)) {
|
||||
$admin_id = $oldMobile->admin_id;
|
||||
} else {
|
||||
// $admin_id = $this->poolUser($order->orderStatus, $order->category_desc);
|
||||
try {
|
||||
$admin_id = \app\server\Orders::poolUser($order->orderStatus, $order->product_id);
|
||||
} catch (\Exception $exception) {
|
||||
Log::info(sprintf('tc create order fail:%s, order_id:%s', $exception->getMessage(), $order->sn));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($admin_id)) return null;
|
||||
$order->admin_id = $admin_id;
|
||||
$order->give_time = time();
|
||||
|
||||
//判断是否需要发短信 200-待使用,205-预约中(抖音),210-已预约(抖音),310-已履约(抖音),300-已完成
|
||||
if (in_array($order->order_status, [200, 205, 210, 310, 300]) ) {
|
||||
$this->sms($admin_id, $order);
|
||||
}
|
||||
$work = \app\server\Orders::getLiveRoomWork($order->create_at, $order->product_id);
|
||||
if ($work) {
|
||||
$order->live_room_work_id = $work->id;
|
||||
}
|
||||
//新获得一个用户的,提示管理员有新的订单
|
||||
Redis::incrBy('CRM:USER:ONLINE:NEW:' . $admin_id, 1);
|
||||
$item = new Orders();
|
||||
}
|
||||
|
||||
if ($order->travel_date && empty($order->travel_end)) {
|
||||
$days = $this->_days($order);
|
||||
if ($days) {
|
||||
$order->travel_end = date('Y-m-d 00:00:00', strtotime($order->travel_date) + $days * 24 * 3600);
|
||||
}
|
||||
}
|
||||
|
||||
if ($item->order_status !== 2 && $order->order_status == 2) {
|
||||
Redis::incrBy('CRM:USER:WRITE:OFF:' . $item->admin_id, 1);
|
||||
}
|
||||
|
||||
$back = $item->save($order->toArray());
|
||||
|
||||
if ($back) {
|
||||
$this->_finance(0, $item->id, $item->asset_price);
|
||||
}
|
||||
}
|
||||
|
||||
// 数据小于50, 结束
|
||||
if (empty($list)) {
|
||||
break;
|
||||
}
|
||||
$page++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function reload($output)
|
||||
{
|
||||
$back = Redis::set('SpiderMt:reload:dy:lock', time(), 'EX', 3600 * 8, 'NX');
|
||||
if (!$back) return;
|
||||
$orders = Orders::where('create_at', '<=', (time() - 15 * 24 * 3600) * 1000)->where('os', 6)->wherein('status', [1, 2, 3])->select();
|
||||
foreach ($orders as $order) {
|
||||
$this->reloadStatus($order->sn, $order->os, $output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $admin_id
|
||||
* @param $order
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function sms($admin_id, $order) {
|
||||
$user = Admins::cache(true)->where('id', $admin_id)->find();
|
||||
$templateId = 261607;
|
||||
if ((!config('app.debug', true) || config('app.debug', true) === 'false') && (time() * 1000 - $order->create_at) / 1000 < 2 * 24 * 3600) {
|
||||
print_r([$order->mobile, $templateId, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
|
||||
$has = Blacks::where('mobile', $order->mobile)->find();
|
||||
if (empty($has) && !empty($order->mobile)) {
|
||||
SMS::juhe_sms_send($order->mobile, $templateId, ['title' => $order->product_name, 'mobile' => $user->mobile]);
|
||||
} else {
|
||||
sleep(10);
|
||||
}
|
||||
} else {
|
||||
print_r([$order->mobile, $templateId, ['title' => $order->product_name, 'mobile' => $user->mobile]]);
|
||||
}
|
||||
}
|
||||
|
||||
private function reloadStatus($orderid, $os, $output)
|
||||
{
|
||||
$w[] = ['sn', '=', $orderid];
|
||||
if ($os) $w[] = ['os', '=', $os];
|
||||
$item = Orders::where($w)->find();
|
||||
if (empty($item)) {
|
||||
$output->writeln('没有找到订单');
|
||||
}
|
||||
|
||||
$m = new Tongcheng($os);
|
||||
$it = $m->get(1, null, null, $item->sn);
|
||||
|
||||
|
||||
if ($it) {
|
||||
$back = $item->save($it[0]);
|
||||
if ($back) {
|
||||
$this->_finance(0, $item->id, $item->asset_price);
|
||||
}
|
||||
} else {
|
||||
$output->writeln('没有拉取到数据:' . $orderid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function _days($order)
|
||||
{
|
||||
if (stripos($order->product_name, '一日') !== false) {
|
||||
return 1;
|
||||
}
|
||||
preg_match('/(\d)天/', $order->product_name, $all);
|
||||
if (!empty($all) && intval($all[1]) > 0) {
|
||||
return $all[1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function zhibo($title, $os = 0)
|
||||
{
|
||||
if ($os == 3) {
|
||||
|
||||
return 2;
|
||||
}
|
||||
if (
|
||||
strlen($title) - 3 == strripos($title, "!")
|
||||
|| strlen($title) - 1 == strripos($title, "!")
|
||||
) {
|
||||
return 1;
|
||||
} elseif (strlen($title) - 3 == strripos($title, "甄")) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function _finance($type = 1, $order_id = 0, $price = 0)
|
||||
{
|
||||
// $fa = Finances::where('order_id', $order_id)->where('type', $type)->find();
|
||||
$back = Redis::set('CRM:ORDER:LOCK:' . $order_id, time(), 'EX', 6, 'NX');
|
||||
if (!$back) return;
|
||||
|
||||
$total = Finances::where('order_id', $order_id)->sum('total'); //总的关于这个订单的金额
|
||||
//如果总金额大于提交上来的核销金额,那就是退费的
|
||||
//如果提交上来的金额小于总金额,那就是核销的
|
||||
if ($total > $price) {
|
||||
$type = 2;
|
||||
$fee = -($total - $price);
|
||||
} elseif ($total < $price) {
|
||||
$type = 1;
|
||||
$fee = $price - $total;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Finances::create([
|
||||
'order_id' => $order_id,
|
||||
'type' => $type,
|
||||
'total' => $fee,
|
||||
'status' => 1
|
||||
]);
|
||||
return;
|
||||
|
||||
// if(empty($fa)) {
|
||||
// if($type == 2) {
|
||||
// $has = Finances::where('order_id', $order_id)->where('type', 1)->find();
|
||||
// if(empty($has)) return;
|
||||
// }
|
||||
// Finances::create([
|
||||
// 'order_id' => $order_id,
|
||||
// 'type' => $type,
|
||||
// 'total' => $price,
|
||||
// 'status' => 1
|
||||
// ]);
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
use app\common\Keys;
|
||||
use support\Redis;
|
||||
|
||||
class FilterMobiles extends base
|
||||
{
|
||||
/**
|
||||
* @param string $mobile
|
||||
* @param bool $refresh
|
||||
* @return bool
|
||||
* @throws \think\db\exception\BindParamException
|
||||
*/
|
||||
public static function isFilterMobile(string $mobile, bool $refresh = false): bool
|
||||
{
|
||||
$key = 'order_filter_mobiles';
|
||||
|
||||
if ($refresh || !Redis::exists($key)) {
|
||||
$list = self::query()->column('id', 'mobile');
|
||||
|
||||
if (!empty($list)) {
|
||||
$chunks = array_chunk($list, 50, true);
|
||||
|
||||
foreach($chunks as $mobiles) {
|
||||
Redis::hMSet($key, $mobiles);
|
||||
}
|
||||
|
||||
Redis::expire($key, 60*60*24);
|
||||
}
|
||||
}
|
||||
|
||||
return Redis::hExists($key, $mobile);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class LiveRoom extends base {
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class LiveRoomWorks extends base {
|
||||
public function zhubo() {
|
||||
return $this->hasOne(Admins::class, 'id', 'zhubo_id')->field(['id', 'name', 'mobile']);
|
||||
}
|
||||
|
||||
public function zhongkong() {
|
||||
return $this->hasOne(Admins::class, 'id', 'zhongkong_id')->field(['id', 'name', 'mobile']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $day
|
||||
* @return LiveRoom[]|array|\think\Collection|\think\db\Query[]|\think\model\Collection
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getDayWork($day, $liveRoomId) {
|
||||
$end = date('Y-m-d', strtotime($day) + 24*60*60);
|
||||
return self::where([
|
||||
['start', '>', $day],
|
||||
['end', '<', $end],
|
||||
'live_room_id' => $liveRoomId,
|
||||
])->with(['zhubo', 'zhongkong'])
|
||||
->order('start', 'desc')
|
||||
->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $roomId
|
||||
* @param $start
|
||||
* @param $end
|
||||
* @return bool
|
||||
*/
|
||||
public static function dayWorkIsExists($id, $roomId, $start, $end) {
|
||||
return self::where([
|
||||
['start', '>', $start],
|
||||
['end', '<', $end],
|
||||
'live_room_id' => $roomId,
|
||||
['id', '!=', $id]
|
||||
])->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $zhuboId
|
||||
* @param $day
|
||||
* @return bool
|
||||
*/
|
||||
public static function zhuboWorkIsExists($id, $zhuboId, $day) {
|
||||
return self::where([
|
||||
'day' => $day,
|
||||
'zhubo_id' => $zhuboId,
|
||||
['id', '!=', $id]
|
||||
])->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $day
|
||||
* @return LiveRoomWorks[]|array|\think\Collection|\think\db\Query[]|\think\model\Collection
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function inWorkAdmin($day) {
|
||||
return self::where(['day' => $day,])->field(['zhubo_id'])->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @param $adminId
|
||||
* @param $start
|
||||
* @param $end
|
||||
*/
|
||||
public static function staticOrder($type, $adminId, $start, $end) {
|
||||
$field = $type == 3 ? 'zhongkong_id' : 'zhubo_id';
|
||||
return self::where([
|
||||
['start', '>', $start],
|
||||
['end', '<', $end],
|
||||
[$field, '=', $adminId]
|
||||
])->fieldRaw('ifnull(sum(orders), 0) as orders')
|
||||
->fieldRaw('ifnull(sum(total), 0) as total')
|
||||
->fieldRaw('ifnull(sum(asset_total), 0) as asset_total')
|
||||
->fieldRaw('ifnull(sum(TIMESTAMPDIFF(HOUR, `start`, `end`)), 0) as work_time')
|
||||
->find();
|
||||
}
|
||||
}
|
|
@ -16,18 +16,21 @@ class Orders extends base
|
|||
const DouyinReservationStatus = [1 => '未预约', 2 => '待接单', 3 => '已预约', 4 => '已取消', 5 => '已完成', 6 => '取消/退款申请'];
|
||||
const DouyinStatus = [1 => '未核销', 2 => '已核销', 3 => '申请退款中', 4 => '已退款', 5 => '部分核销'];
|
||||
|
||||
// 订单状态(100 - 待付款,150-用户取消,200-待使用,205-预约中(抖音),210-已预约(抖音),310-已履约(抖音),300-已完成,400-已关闭)
|
||||
const TongchengStatus = [100 => '待付款', 150 => '用户取消', 200 => '待使用', 205 => '预约中(抖音)', 210 => '已预约(抖音)', 310 => '已履约(抖音)', 300 => '已完成', 400 => '已关闭'];
|
||||
|
||||
const AllOssStatus = [1 => '待使用', 2 => '已核销', 3 => '已退款'];
|
||||
// 全部 未核销 已核销 已退款 未付款 已取消 待支付 申请退款中 部分核销
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
|
||||
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))' //已退款(快手暂无已退款状态)
|
||||
1 => '((os in (1,7) and order_status=3) or (os=2 and order_status=4) or (os in ("3", "5") and order_status=1))', //待使用
|
||||
2 => '((os in (1,7) and order_status=4) or (os=2 and order_status=5) or (os in ("3", "5") and order_status=2))', //已核销
|
||||
3 => '((os in (1,7) and order_status=5) or (os in ("3", "5") and order_status=4))' //已退款(快手暂无已退款状态)
|
||||
];
|
||||
|
||||
const StatusName = ['待跟进', '跟进中', '已核销', '核销失败', '放弃跟单'];
|
||||
const OSS = [1 => '美团', 2 => '快手', 3 => '抖音', 4 => '全平台'];
|
||||
const OSS = [1 => '美团(甄选)', '7' => '美团(新国旅)', 2 => '快手', 3 => '抖音(甄选)', '5' => '抖音(新国旅)', '6' => '同程', 4 => '全平台'];
|
||||
|
||||
const timeType = ['create_time' => '添加记录时间', 'update_time' => '修改记录时间', 'last_follow' => '最后跟进时间', 'next_follow' => '下次跟进时间', 'travel_date' => '出行时间', 'create_at' => '下单时间'];
|
||||
|
||||
|
@ -35,10 +38,14 @@ class Orders extends base
|
|||
|
||||
public function getOrderStatusNameAttr($val)
|
||||
{
|
||||
if ($this->os == 1)
|
||||
if ($this->os == 1 || $this->os == 7)
|
||||
return self::OrderStatus[$this->order_status] ?? '未知';
|
||||
elseif ($this->os == 3)
|
||||
return self::DouyinStatus[$this->order_status] ?? '未知';
|
||||
elseif ($this->os == 5)
|
||||
return self::DouyinStatus[$this->order_status] ?? '未知';
|
||||
elseif ($this->os == 6)
|
||||
return self::TongchengStatus[$this->order_status] ?? '未知';
|
||||
else
|
||||
return self::KuaishouStatus[$this->order_status] ?? '未知';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class Products extends base {
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
namespace app\server;
|
||||
|
||||
use app\model\ThirdMobileLogs;
|
||||
use support\Log;
|
||||
|
||||
/**
|
||||
* 阿里云市场相关接口
|
||||
*/
|
||||
class AliCloudApiService {
|
||||
/**
|
||||
* @param $mobile
|
||||
* @return array|mixed
|
||||
* @throws \think\db\exception\BindParamException
|
||||
*/
|
||||
public function getMobileArea($mobile) {
|
||||
$host = "https://jumcvit.market.alicloudapi.com";
|
||||
$path = "/mobile/area";
|
||||
$method = "POST";
|
||||
$appcode = "4402db67815049c18a55a996b5644f5b";
|
||||
$headers = array();
|
||||
array_push($headers, "Authorization:APPCODE " . $appcode);
|
||||
//根据API的要求,定义相对应的Content-Type
|
||||
array_push($headers, "Content-Type".":"."application/x-www-form-urlencoded; charset=UTF-8");
|
||||
$bodys = "mobile_number=" . $mobile;
|
||||
$url = $host . $path;
|
||||
|
||||
$aliData = $this->curlRequest($url, $method, $headers, $bodys);
|
||||
if (isset($aliData['code']) && $aliData['code'] == 200) {
|
||||
ThirdMobileLogs::query()->insert([
|
||||
'mobile' => $mobile,
|
||||
'area' => $aliData['data']['area'],
|
||||
'originalIsp' => $aliData['data']['originalIsp'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $aliData['data'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param $method
|
||||
* @param $headers
|
||||
* @param $bodys
|
||||
* @return mixed
|
||||
*/
|
||||
private function curlRequest($url, $method, $headers, $bodys){
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_FAILONERROR, false);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
//设定返回信息中是否包含响应信息头,启用时会将响应信息头作为数据流输出,true 表示输出信息头, false表示不输出信息头
|
||||
//如果想将响应结果json字符串转为json数组,建议将 CURLOPT_HEADER 设置成 false
|
||||
curl_setopt($curl, CURLOPT_HEADER, true);
|
||||
if (1 == strpos("$".$url, "https://"))
|
||||
{
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $bodys);
|
||||
curl_close($curl);
|
||||
list($header, $body) = explode("\r\n\r\n", curl_exec($curl), 2);
|
||||
return json_decode($body, true);
|
||||
}
|
||||
}
|
|
@ -15,16 +15,19 @@ class Douyin
|
|||
|
||||
private $cookie = '';
|
||||
private $id = '';
|
||||
private $os = '';
|
||||
private $token = '';
|
||||
private $tryTimes = 0;
|
||||
private $gate = 'https://life.douyin.com';
|
||||
public $totalPage = 6;
|
||||
private $keywords = ['泰国', '普吉岛', '西藏', 'S', 'G', 's', 'g'];
|
||||
|
||||
public function __construct()
|
||||
public function __construct($os = 3)
|
||||
{
|
||||
$this->cookie = $this->_cookie();
|
||||
$this->id = $this->_id();
|
||||
$this->token = $this->_token();
|
||||
$this->os = $os;
|
||||
$this->token = $this->_token('', $os);
|
||||
}
|
||||
|
||||
public function login()
|
||||
|
@ -86,7 +89,6 @@ class Douyin
|
|||
|
||||
if ($list && $list->status_code == 0 && !empty($list->order_list)) {
|
||||
foreach ($list->order_list as $order) {
|
||||
echo "抖音 订单号:{$order->order_info->order_id} \n\n";
|
||||
if (empty($cert)) {
|
||||
$cert = $this->_cert($order->order_info->order_id);
|
||||
if (empty($cert)) {
|
||||
|
@ -160,18 +162,19 @@ class Douyin
|
|||
$this->totalPage = 1;
|
||||
return $_list;
|
||||
}
|
||||
echo date('Y-m-d', $start) . '--' . date('Y-m-d', $end) . " 抖音 page:{$page} total_count:" . $list->data->pagination->total_count . " page_count:" . $list->data->pagination->page_count . " \n\n";
|
||||
Log::info(date('Y-m-d', $start) . '--' . date('Y-m-d', $end) . " 抖音 page:{$page} total_count:" . $list->data->pagination->total_count . " page_count:" . $list->data->pagination->page_count );
|
||||
|
||||
foreach ($list->data->list as $order) {
|
||||
echo "抖音 订单号:{$order->order_id} \n\n";
|
||||
Log::info("抖音 订单号:{$order->order_id} \n\n");
|
||||
|
||||
$orderDetail = $this->_orderDetail($order->order_id, $order->status);
|
||||
if (empty($orderDetail)) {
|
||||
Log::info("抖音 订单详情拉取失败:{$order->order_id}");
|
||||
throw new Exception("抖音拉单{$order->order_id}详情获取失败");
|
||||
}
|
||||
|
||||
$item = new Orders();
|
||||
$item->os = 3;
|
||||
$item->os = $this->os;
|
||||
$item->sn = $order->order_id;
|
||||
$item->product_id = $order->sku->sku_id;
|
||||
$item->product_name = $order->sku->title;
|
||||
|
@ -187,8 +190,8 @@ class Douyin
|
|||
$item->asset_status = $order->status ?? 0;
|
||||
$item->category_desc = $order->sku->title;
|
||||
$item->asset_price = $order->amount->verify_amount ?? 0;
|
||||
$item->is_refunded = $item->order_status == 4 ? 1 : 0; // 是否已退款(1-已退款,0-未退款)
|
||||
//抖音的核销金额需要查核销订单
|
||||
|
||||
if ($item->create_at < strtotime('2024-05-22') * 1000) {
|
||||
$this->totalPage = 1;
|
||||
break;
|
||||
|
@ -211,7 +214,7 @@ class Douyin
|
|||
}
|
||||
}
|
||||
|
||||
if (!$kw_match) {
|
||||
if ($this->os == 3 && !$kw_match) {
|
||||
printf("skip order: %s %s\n", $item->sn, $item->product_name);
|
||||
continue;
|
||||
}
|
||||
|
@ -280,64 +283,10 @@ class Douyin
|
|||
return true;
|
||||
}
|
||||
|
||||
/*public function _killMiddleProcesses($pattern, $maxProcesses)
|
||||
{
|
||||
// 检查操作系统是否为 Linux
|
||||
if (PHP_OS !== 'Linux') {
|
||||
echo "当前系统不是 Linux,跳过执行。\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取当前进程的 PID
|
||||
$currentPid = getmypid();
|
||||
|
||||
// 获取进程列表
|
||||
$command = "ps aux | grep '$pattern' | grep -v grep | grep -v '$currentPid' | awk '{print $2,$10}'";
|
||||
$output = [];
|
||||
exec($command, $output);
|
||||
|
||||
$processCount = count($output);
|
||||
|
||||
// 如果进程数量超过最大值,则终止中间部分的 x-12 个进程
|
||||
if ($processCount > $maxProcesses) {
|
||||
$processesToKill = $processCount - $maxProcesses;
|
||||
$processesKilled = 0;
|
||||
|
||||
// 找到运行时间最短的不包含自己的进程并终止它
|
||||
foreach ($output as $line) {
|
||||
list($pid, $runtime) = explode(' ', $line);
|
||||
$runtimeParts = explode(':', $runtime);
|
||||
$hours = intval($runtimeParts[0]);
|
||||
$minutes = intval($runtimeParts[1]);
|
||||
$totalMinutes = $hours * 60 + $minutes;
|
||||
|
||||
|
||||
if ($pid !== $currentPid) {
|
||||
exec("kill -9 $pid");
|
||||
$processesKilled++;
|
||||
|
||||
echo "Killed process with PID: $pid\n";
|
||||
|
||||
if ($processesKilled >= $processesToKill) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($processesKilled === 0) {
|
||||
echo "No processes to kill.\n";
|
||||
}
|
||||
} else {
|
||||
echo "Process count is not over the limit.\n";
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
|
||||
public function _killMiddleProcesses($pattern, $maxProcesses)
|
||||
{
|
||||
// 检查操作系统是否为 Linux
|
||||
if (PHP_OS !== 'Linux') {
|
||||
echo "当前系统不是 Linux,跳过执行。\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -388,14 +337,14 @@ class Douyin
|
|||
exec("kill -9 {$process['pid']}");
|
||||
$processesKilled++;
|
||||
|
||||
echo "Killed process with PID: {$process['pid']}\n";
|
||||
Log::info("Killed process with PID: {$process['pid']}\n");
|
||||
}
|
||||
|
||||
if ($processesKilled === 0) {
|
||||
echo "No processes to kill.\n";
|
||||
Log::info( "No processes to kill.\n");
|
||||
}
|
||||
} else {
|
||||
echo "Process count is not over the limit.\n";
|
||||
Log::info( "Process count is not over the limit.\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -408,7 +357,7 @@ class Douyin
|
|||
'order_id' => $orderId,
|
||||
'book_order_id' => '',
|
||||
'status' => $status,
|
||||
'root_life_account_id' => $this->_id()
|
||||
'root_life_account_id' => $this->_id($this->os)
|
||||
]);
|
||||
return $orderDetail->data ?? null;
|
||||
}
|
||||
|
@ -486,22 +435,31 @@ class Douyin
|
|||
|
||||
public function _cookie($data = '')
|
||||
{
|
||||
if ($data) Redis::set('Douyin:cookie', $data);
|
||||
return Redis::get('Douyin:cookie');
|
||||
$key = 'Douyin:cookie';
|
||||
if ($this->os == 5) {
|
||||
$key = 'Douyin:cookie-' . $this->os;
|
||||
}
|
||||
if ($data) Redis::set($key, $data);
|
||||
return Redis::get($key);
|
||||
}
|
||||
|
||||
public function _id($id = '')
|
||||
{
|
||||
if ($id) Redis::set('Douyin:id', $id);
|
||||
return Redis::get('Douyin:id');
|
||||
if ($this->os == 5) {
|
||||
return '7399206501845403686';
|
||||
} else {
|
||||
// return '7259680722519066679';
|
||||
return Redis::get('Douyin:id');
|
||||
}
|
||||
}
|
||||
|
||||
protected $_try = 0;
|
||||
|
||||
public function _token($token = '')
|
||||
{
|
||||
if ($token) Redis::set('Douyin:token', $token, 'ex', 3600 * 24 - 50);
|
||||
$token = Redis::get('Douyin:token');
|
||||
$dyKey = sprintf('Douyin:token-%s', $this->os);
|
||||
if ($token) Redis::set($dyKey, $token, 'ex', 3600 * 24 - 50);
|
||||
$token = Redis::get($dyKey);
|
||||
if (empty($token) && $this->_cookie()) {
|
||||
if ($this->_try > 3) {
|
||||
throw new Exception("cookie 失效");
|
||||
|
@ -509,10 +467,10 @@ class Douyin
|
|||
$id = $this->_curl('/life/gate/v1/user/detail', null, 'HEAD');
|
||||
$this->_try++;
|
||||
if ($id) {
|
||||
Redis::set('Douyin:token', $id, 'ex', 3600 * 24 - 50);
|
||||
Redis::set($dyKey, $id, 'ex', 3600 * 24 - 50);
|
||||
}
|
||||
}
|
||||
return Redis::get('Douyin:token');
|
||||
return Redis::get($dyKey);
|
||||
}
|
||||
|
||||
public function _curl($url, $params, $method = 'GET')
|
||||
|
@ -523,6 +481,7 @@ class Douyin
|
|||
if ($requestCount == 1) {
|
||||
Redis::expire($key, 604800); // 7天
|
||||
}
|
||||
$cookiePath = sprintf(runtime_path() . '%s_dy_cookie.txt', $this->os);
|
||||
|
||||
$http = $this->gate . $url;
|
||||
if ($method == 'GET') {
|
||||
|
@ -540,21 +499,22 @@ class Douyin
|
|||
'Content-type: application/json'
|
||||
];
|
||||
if ($method == 'GET' || $method == 'POST') {
|
||||
$header[] = 'x-secsdk-csrf-token: ' . $this->_token();
|
||||
$header[] = 'x-secsdk-csrf-token: ' . $this->_token('', $this->os);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiePath);
|
||||
$header[] = 'x-secsdk-csrf-request: 1';
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_COOKIE, $this->_cookie());
|
||||
curl_setopt($ch, CURLOPT_COOKIE, $this->_cookie(''));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiePath);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
|
||||
}
|
||||
|
||||
$body = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
if (!in_array($method, ['GET', 'POST'])) {
|
||||
|
@ -569,6 +529,16 @@ class Douyin
|
|||
}
|
||||
return null;
|
||||
}
|
||||
return json_decode($body);
|
||||
|
||||
$res = json_decode($body);
|
||||
if (!isset($res->status_code) || $res->status_code != 0) {
|
||||
Log::info("抖音 os:{$this->os} \n\n");
|
||||
Log::info( "抖音 body:{$body} \n\n");
|
||||
if ($this->tryTimes > 1) {
|
||||
(new ThirdApiService())->weComNotice($this->os);
|
||||
}
|
||||
$this->tryTimes++;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
|
@ -10,9 +10,12 @@ class Meituan {
|
|||
private $cookie = '';
|
||||
private $gate = 'https://lvyou.meituan.com';
|
||||
public $totalPage = 6;
|
||||
public $os;
|
||||
private $tryTimes = 0;
|
||||
|
||||
public function __construct()
|
||||
public function __construct($os = 1)
|
||||
{
|
||||
$this->os = $os;
|
||||
$this->cookie = $this->_cookie();
|
||||
}
|
||||
|
||||
|
@ -51,7 +54,7 @@ class Meituan {
|
|||
|
||||
$params = [
|
||||
'page' => $page,
|
||||
'count' => 20,
|
||||
'count' => 50,
|
||||
'orderId' => $orderId,
|
||||
'productId' => '',
|
||||
'orderTimeTo' => $end ?? '',
|
||||
|
@ -74,21 +77,21 @@ class Meituan {
|
|||
}
|
||||
}
|
||||
|
||||
if($list && $list->code == 0) {
|
||||
$this->totalPage = $list->data->totalPage;
|
||||
}
|
||||
|
||||
$_list = [];
|
||||
if($list && $list->code == 0 && $list->data && is_array($list->data->orderList)) {
|
||||
foreach($list->data->orderList as $order) {
|
||||
echo "美团 订单号:{$order->orderId} \n\n";
|
||||
Log::info("美团 订单号:{$order->orderId} \n\n");
|
||||
|
||||
if(in_array($order->orderId, ['',''])) {
|
||||
echo "异常订单:". $order->orderId;
|
||||
Log::info( "异常订单:". $order->orderId);
|
||||
sleep(100);
|
||||
}
|
||||
// 过滤未付款订单
|
||||
if ($order->orderStatus == 1) {
|
||||
continue;
|
||||
}
|
||||
$item = new Orders();
|
||||
$item->os = 1;
|
||||
$item->os = $this->os;
|
||||
$item->sn = $order->orderId;
|
||||
$item->product_id = $order->productId;
|
||||
$item->product_name = $order->productName;
|
||||
|
@ -103,7 +106,9 @@ class Meituan {
|
|||
$item->order_status = $order->orderStatus;
|
||||
$item->refund_status = $order->refundStatus;
|
||||
$item->category_desc = $order->categoryDesc;
|
||||
$item->is_zhibo = $order->bizOrderChannel == 5 ? 1 : 0; // 是否直播(5-视频号,3-小程序)
|
||||
$item->asset_price = $order->orderStatus == 4 ? $order->totalPrice: 0; //已核销订单,核销金额为订单金额
|
||||
$item->is_refunded = $item->order_status == 5 ? 1 : 0; // 是否已退款(1-已退款,0-未退款)
|
||||
|
||||
$_list[] = $item;
|
||||
}
|
||||
|
@ -113,8 +118,9 @@ class Meituan {
|
|||
}
|
||||
|
||||
public function _cookie($data = '') {
|
||||
if($data) Redis::set('Meituan:cookie', $data);
|
||||
return Redis::get('Meituan:cookie');
|
||||
$key = sprintf('Meituan:cookie-%s', $this->os);
|
||||
if($data) Redis::set($key, $data);
|
||||
return Redis::get($key);
|
||||
}
|
||||
|
||||
public function _curl($url, $params, $method = 'GET') {
|
||||
|
@ -149,8 +155,19 @@ class Meituan {
|
|||
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
|
||||
}
|
||||
|
||||
echo $body = curl_exec($ch);
|
||||
$body = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return json_decode($body);
|
||||
$res = json_decode($body);
|
||||
if (!isset($res->code) || $res->code != 0) {
|
||||
Log::info('os:' . $this->os);
|
||||
Log::info('http:' . $http);
|
||||
Log::info('body:' . $body);
|
||||
|
||||
if ($this->tryTimes > 1) {
|
||||
(new ThirdApiService())->weComNotice($this->os);
|
||||
}
|
||||
$this->tryTimes++;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
|
@ -3,20 +3,27 @@
|
|||
namespace app\server;
|
||||
|
||||
use app\common\Error;
|
||||
use app\model\Admins;
|
||||
use app\model\Admins as AdminsModel;
|
||||
use app\model\Blacks;
|
||||
use app\model\FilterMobiles;
|
||||
use app\model\Finances as FinancesModel;
|
||||
use app\model\LiveRoomWorks;
|
||||
use app\model\Orders as OrdersModel;
|
||||
use app\model\Products;
|
||||
use support\Log;
|
||||
use support\Redis;
|
||||
|
||||
class Orders {
|
||||
protected static $redisPool = [];
|
||||
public static function isDaishiyong(OrdersModel $order): bool
|
||||
{
|
||||
// 根据 OrdersModel::AllOssStatusSql[1] 进行判断
|
||||
// ((os=1 and order_status=3) or (os=2 and order_status=4) or (os=3 and order_status=1))
|
||||
return $order->os == 1 && $order->order_status == 3
|
||||
|| $order->os == 2 && $order->order_status == 4
|
||||
|| $order->os == 3 && $order->order_status == 1;
|
||||
return (in_array($order->os, [1, 7]) && $order->order_status == 3)
|
||||
|| ($order->os == 2 && $order->order_status == 4)
|
||||
|| (in_array($order->os, [3, 5]) && in_array($order->order_status, [1, 5]))
|
||||
|| (in_array($order->os, [6]) && in_array($order->order_status, [200]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,6 +49,7 @@ class Orders {
|
|||
$ttl = 86400;
|
||||
$result = [];
|
||||
|
||||
$admins = array_column($admins, null, 'id');
|
||||
foreach($orders as $order) {
|
||||
$admin_mobile = $admins[$order->admin_id] ?? '';
|
||||
|
||||
|
@ -49,6 +57,11 @@ class Orders {
|
|||
$result[$order->sn] = Error::undefined('client mobile or admin mobile invalid');
|
||||
continue;
|
||||
}
|
||||
if (FilterMobiles::isFilterMobile($order->mobile)) {
|
||||
$result[$order->sn] = Error::undefined('刷单账单');
|
||||
continue;
|
||||
}
|
||||
$admin_mobile = $admin_mobile['mobile'] ?? '';
|
||||
|
||||
if (Blacks::CExists($order->mobile)) {
|
||||
$result[$order->sn] = Error::ERR_SMS_BLACKS;
|
||||
|
@ -85,7 +98,8 @@ class Orders {
|
|||
|
||||
switch ($order->os) {
|
||||
case 1:
|
||||
$mt = new Meituan();
|
||||
case 7:
|
||||
$mt = new Meituan($order->os);
|
||||
$it = $mt->get(1, null, null, $order->sn);
|
||||
|
||||
if ($it) {
|
||||
|
@ -94,13 +108,14 @@ class Orders {
|
|||
|
||||
break;
|
||||
case 3:
|
||||
$dy = new Douyin();
|
||||
case 5:
|
||||
$dy = new Douyin($order->os);
|
||||
$it = $dy->get(1, null, null, $order->sn);
|
||||
if ($it) {
|
||||
$got = $it[0];
|
||||
|
||||
// 查询未预约状态
|
||||
if ($order->appointment_status != 1) {
|
||||
if ($order->appointment_status == 0) {
|
||||
$appointment_status = $dy->_orderMakeAppointmentStatus($order->sn);
|
||||
if ($appointment_status !== null) {
|
||||
$got['appointment_status'] = 1;
|
||||
|
@ -108,6 +123,14 @@ class Orders {
|
|||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 6:
|
||||
$tc = new Tongcheng($order->os);
|
||||
$it = $tc->get(1, null, null, $order->sn);
|
||||
|
||||
if ($it) {
|
||||
$got = $it[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -165,7 +188,8 @@ class Orders {
|
|||
$got = null;
|
||||
switch ($order->os) {
|
||||
case 1:
|
||||
$mt = new Meituan();
|
||||
case 7:
|
||||
$mt = new Meituan($order->os);
|
||||
$it = $mt->get(1, null, null, $order->sn);
|
||||
|
||||
if ($it) {
|
||||
|
@ -174,7 +198,8 @@ class Orders {
|
|||
|
||||
break;
|
||||
case 3:
|
||||
$dy = new Douyin();
|
||||
case 5:
|
||||
$dy = new Douyin($order->os);
|
||||
$it = $dy->get(1, null, null, $order->sn);
|
||||
if ($it) {
|
||||
$got = $it[0];
|
||||
|
@ -196,4 +221,134 @@ class Orders {
|
|||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $orderCreateTime
|
||||
* @param $productId
|
||||
* @return array|mixed|\think\Model
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getLiveRoomWork(int $orderCreateTime, $productId) {
|
||||
$orderCreateDate = date('Y-m-d H:i:s', $orderCreateTime/1000);
|
||||
$roomWork = LiveRoomWorks::join('live_room', 'live_room_works.live_room_id=live_room.id')
|
||||
->where('live_room_works.start', '<', $orderCreateDate)
|
||||
->where('live_room_works.end', '>', $orderCreateDate)
|
||||
->whereRaw(sprintf('find_in_set(%s, live_room.`product_ids`)', $productId))
|
||||
->field(['live_room_works.id'])
|
||||
->find();
|
||||
|
||||
return $roomWork;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配用户
|
||||
* @param int $status
|
||||
* @param string $categoryDesc
|
||||
* @return int|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function poolUser($status, $thirdProductId) {
|
||||
$status .= $thirdProductId;
|
||||
if (empty(self::$redisPool[$status])) {
|
||||
self::$redisPool[$status] = Redis::hGetAll('CRM:Pool:' . $status);
|
||||
$users = self::users($thirdProductId);
|
||||
shuffle($users);
|
||||
$_users = [];
|
||||
if (empty(self::$redisPool[$status])) {
|
||||
foreach ($users as $user) {
|
||||
$_users[$user->username] = 0;
|
||||
Redis::hSet('CRM:Pool:' . $status, $user->username, 0);
|
||||
}
|
||||
self::$redisPool[$status] = $_users;
|
||||
} else {
|
||||
asort(self::$redisPool[$status]);
|
||||
$key_users = array_keys(self::$redisPool[$status]);
|
||||
$username = $key_users[0];
|
||||
$max = self::$redisPool[$status][$username];
|
||||
$_users = [];
|
||||
foreach ($users as $user) {
|
||||
$_users[] = $user->username;
|
||||
if (!in_array($user->username, $key_users)) {
|
||||
self::$redisPool[$status][$username] = $max;
|
||||
Redis::hSet('CRM:Pool:' . $status, $user->username, $max);
|
||||
}
|
||||
}
|
||||
foreach (self::$redisPool[$status] as $username => $val) {
|
||||
if (!in_array($username, $_users)) {
|
||||
unset(self::$redisPool[$status][$username]);
|
||||
Redis::hDel('CRM:Pool:' . $status, $username);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$username = null;
|
||||
try {
|
||||
$pool = self::$redisPool[$status];
|
||||
if (empty($pool)) $pool = [];
|
||||
asort($pool);
|
||||
$keys = array_keys($pool);
|
||||
if (empty($keys)) {
|
||||
throw new \Exception('没有可以分配的用户');
|
||||
}
|
||||
$username = $keys[0];
|
||||
self::$redisPool[$status][$username] += 1;
|
||||
Redis::hIncrBy('CRM:Pool:' . $status, $username, 1);
|
||||
} catch (\Exception $e) {
|
||||
Log::error(dirname(__FILE__) . __LINE__ . '没有可以分配的用户', func_get_args());
|
||||
throw new \Exception('没有可以分配的用户');
|
||||
}
|
||||
return $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $thirdProductId
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
protected static function users($thirdProductId) {
|
||||
$product = Products::query()->where('third_product_id', $thirdProductId)->find();
|
||||
if (empty($product)) {
|
||||
return [];
|
||||
}
|
||||
$users = Admins::where('status', 1)->where('is_order', 1)->whereFindInSet('product_ids', $product->id)->select();
|
||||
$us = [];
|
||||
foreach ($users as $u) {
|
||||
$ru = Redis::get('CRM:USER:ONLINE:' . $u->id);
|
||||
if (empty($ru)) continue;
|
||||
|
||||
$_u = new \stdClass();
|
||||
$_u->username = $u->id;
|
||||
$us[] = $_u;
|
||||
};
|
||||
|
||||
return $us;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送订单短信
|
||||
* @param $admin_id
|
||||
* @param $order
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function sendOrderSms($admin_id, $order)
|
||||
{
|
||||
$user = Admins::cache(true)->where('id', $admin_id)->find();
|
||||
if ((!config('app.debug', true) || config('app.debug', true) === 'false') && (time() * 1000 - $order->create_at) / 1000 < 2 * 24 * 3600) {
|
||||
$has = Blacks::where('mobile', $order->mobile)->find();
|
||||
if (empty($has) && !empty($order->mobile)) {
|
||||
SMS::juhe_sms_send($order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]);
|
||||
} else {
|
||||
sleep(10);
|
||||
}
|
||||
} else {
|
||||
Log::info('订单未发送短息:' . json_encode([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
namespace app\server;
|
||||
|
||||
use app\model\Orders;
|
||||
use app\model\ThirdMobileLogs;
|
||||
use http\Client;
|
||||
use support\Log;
|
||||
use support\Redis;
|
||||
|
||||
/**
|
||||
* 阿里云市场相关接口
|
||||
*/
|
||||
class ThirdApiService {
|
||||
private $notice = '%s token已过期,请及时更换';
|
||||
|
||||
/**
|
||||
* @param $mobile
|
||||
* @return array|mixed
|
||||
* @throws \think\db\exception\BindParamException
|
||||
*/
|
||||
public function getMobileArea($mobile) {
|
||||
$host = "https://jumcvit.market.alicloudapi.com";
|
||||
$path = "/mobile/area";
|
||||
$method = "POST";
|
||||
$appcode = "4402db67815049c18a55a996b5644f5b";
|
||||
$headers = array();
|
||||
array_push($headers, "Authorization:APPCODE " . $appcode);
|
||||
//根据API的要求,定义相对应的Content-Type
|
||||
array_push($headers, "Content-Type".":"."application/x-www-form-urlencoded; charset=UTF-8");
|
||||
$bodys = "mobile_number=" . $mobile;
|
||||
$url = $host . $path;
|
||||
|
||||
$aliData = $this->curlMobileRequest($url, $method, $headers, $bodys);
|
||||
if (isset($aliData['code']) && $aliData['code'] == 200) {
|
||||
ThirdMobileLogs::query()->insert([
|
||||
'mobile' => $mobile,
|
||||
'area' => $aliData['data']['area'],
|
||||
'originalIsp' => $aliData['data']['originalIsp'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $aliData['data'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param $method
|
||||
* @param $headers
|
||||
* @param $bodys
|
||||
* @return mixed
|
||||
*/
|
||||
private function curlMobileRequest($url, $method, $headers, $bodys){
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_FAILONERROR, false);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
//设定返回信息中是否包含响应信息头,启用时会将响应信息头作为数据流输出,true 表示输出信息头, false表示不输出信息头
|
||||
//如果想将响应结果json字符串转为json数组,建议将 CURLOPT_HEADER 设置成 false
|
||||
curl_setopt($curl, CURLOPT_HEADER, true);
|
||||
if (1 == strpos("$".$url, "https://"))
|
||||
{
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $bodys);
|
||||
curl_close($curl);
|
||||
list($header, $body) = explode("\r\n\r\n", curl_exec($curl), 2);
|
||||
return json_decode($body, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企微提醒
|
||||
* @param $message
|
||||
* @return mixed
|
||||
*/
|
||||
public function weComNotice($os) {
|
||||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=e138b55c-2886-4729-922e-9a8c753f75ee';
|
||||
$method = 'POST';
|
||||
$message = sprintf($this->notice, Orders::OSS[$os] ?? '');
|
||||
$bodys = [
|
||||
'msgtype' => 'text',
|
||||
'text' => [
|
||||
'content' => $message
|
||||
]
|
||||
];
|
||||
|
||||
$key = 'notice-' . $os;
|
||||
if (Redis::setNx($key, 1)) {
|
||||
Redis::expire($key, 600); // 十分钟内通知一次
|
||||
$this->curlRequest($url, $method, [], $bodys);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param $method
|
||||
* @param $headers
|
||||
* @param $bodys
|
||||
* @return mixed
|
||||
*/
|
||||
private function curlRequest($url, $method, $headers, $bodys){
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_FAILONERROR, false);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
//设定返回信息中是否包含响应信息头,启用时会将响应信息头作为数据流输出,true 表示输出信息头, false表示不输出信息头
|
||||
//如果想将响应结果json字符串转为json数组,建议将 CURLOPT_HEADER 设置成 false
|
||||
curl_setopt($curl, CURLOPT_HEADER, true);
|
||||
if (1 == strpos("$".$url, "https://"))
|
||||
{
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
}
|
||||
if (is_array($bodys)) {
|
||||
$bodys = json_encode($bodys);
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $bodys);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, array_merge($headers, [
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($bodys)
|
||||
]));
|
||||
curl_close($curl);
|
||||
list($header, $body) = explode("\r\n\r\n", curl_exec($curl), 2);
|
||||
Log::info(sprintf('third req:%s, res:%s', $url, $body));
|
||||
return json_decode($body, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
|
||||
namespace app\server;
|
||||
|
||||
use app\model\Orders;
|
||||
use Exception;
|
||||
use support\Log;
|
||||
use support\Redis;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
|
||||
/**
|
||||
* 同程订单处理
|
||||
*/
|
||||
class Tongcheng {
|
||||
private $os = '';
|
||||
protected $cookie;
|
||||
private $gate = 'https://ebk.17u.cn';
|
||||
public $totalPage = 6;
|
||||
private $tryTimes = 0;
|
||||
|
||||
public function __construct($os = 6)
|
||||
{
|
||||
$this->cookie = $this->_cookie();
|
||||
$this->os = $os;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get($page, $start = null, $end = null, $orderId = '')
|
||||
{
|
||||
if (empty($start) || empty($end)) {
|
||||
$start = date('Y-m-d 00:00:00');
|
||||
$end = date('Y-m-d 23:59:59');
|
||||
}
|
||||
|
||||
if ($orderId) {
|
||||
$start = $end = null;
|
||||
}
|
||||
|
||||
$_list = [];
|
||||
$list = $this->_certificateList($start, $end, $page, $orderId);
|
||||
if (empty($list->data->list) || $list->code != 1000) {
|
||||
$this->totalPage = 1;
|
||||
return $_list;
|
||||
}
|
||||
Log::info($start . '--' . $end . " 同程 page:{$page} total_count:" . $list->data->totalCount . " page_count:" . $list->data->totalPage);
|
||||
|
||||
foreach ($list->data->list as $order) {
|
||||
Log::info("同程 订单号:{$order->orderId} \n\n");
|
||||
|
||||
// $orderDetail = $this->_orderDetail($order->order_id, $order->status);
|
||||
// if (empty($orderDetail)) {
|
||||
// Log::info("同程 订单详情拉取失败:{$order->order_id}");
|
||||
// throw new Exception("同程拉单{$order->order_id}详情获取失败");
|
||||
// }
|
||||
|
||||
$order->orderAmount = $order->orderAmount * 100;
|
||||
$item = new Orders();
|
||||
$item->os = $this->os;
|
||||
$item->sn = $order->orderId;
|
||||
$item->product_id = $order->lineId;
|
||||
$item->product_name = $order->title;
|
||||
$item->category_id = $order->agentFinderId;
|
||||
$item->category_desc = $order->agentFinderNickname;
|
||||
$item->create_at = strtotime($order->createTime) * 1000;
|
||||
$item->mobile = $order->mobile;
|
||||
$item->travel_date = null;
|
||||
$item->total_price = $order->orderAmount;
|
||||
$item->quantity = $order->productCount;
|
||||
$item->unit_price = $order->orderAmount/$order->productCount;
|
||||
$item->actual_price = $order->orderAmount;
|
||||
$item->order_status = $order->orderStatus; // 订单状态(100 - 待付款,150-用户取消,200-待使用,205-预约中(抖音),210-已预约(抖音),310-已履约(抖音),300-已完成,400-已关闭)
|
||||
$item->appointment_status = in_array($order->orderStatus, [210, 310, 300]) ? 1 : 0; // 是否已预约
|
||||
|
||||
$item->asset_status = $order->status ?? 0; // 核销状态
|
||||
$item->asset_price = $order->orderStatus == 300 ? $order->orderAmount : 0; // 核销金额
|
||||
//抖音的核销金额需要查核销订单
|
||||
|
||||
$_list[] = $item;
|
||||
}
|
||||
|
||||
return $_list; //返回半成品
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function _certificateList($start, $end, $page, $orderId)
|
||||
{
|
||||
$params = [
|
||||
'begin' => $start,// 开始时间,格式:2024-09-01 00:00:00
|
||||
'end' => $end,// 结束时间 格式:2024-09-01 00:00:00
|
||||
'orderStatus' => "",// 订单状态
|
||||
'current' => $page, // 当前数量
|
||||
'size' => 50, // 显示数量
|
||||
'orderId' => $orderId, // 订单号
|
||||
];
|
||||
$this->_getRetriesLock();
|
||||
|
||||
$list = $this->_curl("/merchant/api/market-live/wxVideoV2/supplierOrderByPage", $params, 'POST');
|
||||
if (empty($list) || $list->code != 1000) {
|
||||
$this->_getRetriesLock();
|
||||
$list = $this->_curl("/merchant/api/market-live/wxVideoV2/supplierOrderByPage", $params, 'POST');
|
||||
if (empty($list) || $list->code != 1000) {
|
||||
Log::error('===查询时间:' . $start . '--' . $end . '====certificate_list: ' . json_encode($list));
|
||||
throw new Exception("同程拉单失败,Err:" . json_encode($list));
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function _getRetriesLock(): bool
|
||||
{
|
||||
$pattern = 'php webman spider:tc';
|
||||
$maxProcesses = 8;
|
||||
$this->_killMiddleProcesses($pattern, $maxProcesses);
|
||||
|
||||
|
||||
$maxRetries = 5;
|
||||
$retries = 0;
|
||||
while ($retries < $maxRetries) {
|
||||
$back = Redis::set('SpiderTc:CertificateList:lock', time(), 'EX', 1, 'NX');
|
||||
if ($back) {
|
||||
break;
|
||||
}
|
||||
$retries++;
|
||||
sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function _killMiddleProcesses($pattern, $maxProcesses)
|
||||
{
|
||||
// 检查操作系统是否为 Linux
|
||||
if (PHP_OS !== 'Linux') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取当前进程的 PID
|
||||
$currentPid = getmypid();
|
||||
|
||||
// 获取进程列表
|
||||
$command = "ps aux | grep '$pattern' | grep -v grep | grep -v '$currentPid' | awk '{print $2,$10}'";
|
||||
$output = [];
|
||||
exec($command, $output);
|
||||
|
||||
$processList = [];
|
||||
foreach ($output as $line) {
|
||||
list($pid, $runtime) = explode(' ', $line);
|
||||
$runtimeParts = explode(':', $runtime);
|
||||
$hours = intval($runtimeParts[0]);
|
||||
$minutes = intval($runtimeParts[1]);
|
||||
$totalMinutes = $hours * 60 + $minutes;
|
||||
$processList[] = ['pid' => $pid, 'runtime' => $totalMinutes];
|
||||
}
|
||||
|
||||
// 按运行时间排序
|
||||
usort($processList, function ($a, $b) {
|
||||
return $a['runtime'] <=> $b['runtime'];
|
||||
});
|
||||
|
||||
// 过滤掉当前 PID 和运行时间最短的 PID
|
||||
if (!empty($processList)) {
|
||||
array_shift($processList); // 移除运行时间最短的
|
||||
}
|
||||
$processList = array_filter($processList, function ($process) use ($currentPid) {
|
||||
return $process['pid'] != $currentPid;
|
||||
});
|
||||
|
||||
$processCount = count($processList);
|
||||
|
||||
// 如果进程数量超过最大值,则终止中间部分的进程
|
||||
if ($processCount > $maxProcesses) {
|
||||
$processesToKill = $processCount - $maxProcesses;
|
||||
$processesKilled = 0;
|
||||
|
||||
// 杀死运行时间最短的进程
|
||||
foreach ($processList as $process) {
|
||||
if ($processesKilled >= $processesToKill) {
|
||||
break;
|
||||
}
|
||||
|
||||
exec("kill -9 {$process['pid']}");
|
||||
$processesKilled++;
|
||||
|
||||
Log::info("Killed process with PID: {$process['pid']}\n");
|
||||
}
|
||||
|
||||
if ($processesKilled === 0) {
|
||||
Log::info("No processes to kill.\n");
|
||||
}
|
||||
} else {
|
||||
Log::info("Process count is not over the limit.\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function _orderDetail($orderId = null, $status = null)
|
||||
{
|
||||
$orderDetail = $this->_curl("/life/trip/v2/travel_agency/book/detail", [
|
||||
'order_id' => $orderId,
|
||||
'book_order_id' => '',
|
||||
'status' => $status,
|
||||
]);
|
||||
return $orderDetail->data ?? null;
|
||||
}
|
||||
|
||||
public function _cookie($data = '')
|
||||
{
|
||||
$key = 'Tongcheng:cookie-' . $this->os;
|
||||
if ($data) Redis::set($key, $data);
|
||||
return Redis::get($key);
|
||||
}
|
||||
|
||||
public function _curl($url, $params, $method = 'GET')
|
||||
{
|
||||
// 请求次数统计
|
||||
$key = sprintf("Tongcheng:ReqiestCount:%s", date('Y-m-d'));
|
||||
$requestCount = Redis::incrBy($key, 1);
|
||||
if ($requestCount == 1) {
|
||||
Redis::expire($key, 604800); // 7天
|
||||
}
|
||||
$cookiePath = sprintf(runtime_path() . '%s_tc_cookie.txt', $this->os);
|
||||
|
||||
$http = $this->gate . $url;
|
||||
if ($method == 'GET') {
|
||||
$http = $http . '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
$ch = curl_init($http);
|
||||
|
||||
$header = [
|
||||
'Content-type: application/json'
|
||||
];
|
||||
if ($method == 'GET' || $method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiePath);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_COOKIE, $this->_cookie());
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiePath);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
|
||||
}
|
||||
$body = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
if (!in_array($method, ['GET', 'POST'])) {
|
||||
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$header = substr($body, 0, $header_size);
|
||||
$in = explode("\n", $body);
|
||||
foreach ($in as $i) {
|
||||
if (stripos($i, 'x-ware-csrf-token') !== false) {
|
||||
$inn = explode(',', $i);
|
||||
return $inn[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
$res = json_decode($body);
|
||||
if (!isset($res->code) || $res->code != 1000) {
|
||||
Log::info( "同城 body:{$body} \n\n");
|
||||
if ($this->tryTimes > 1) {
|
||||
(new ThirdApiService())->weComNotice($this->os);
|
||||
}
|
||||
$this->tryTimes++;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
.app-container[data-v-45d18cae]{position:relative;padding-bottom:60px}.el-table[data-v-45d18cae],.filter-container[data-v-45d18cae]{padding-bottom:52px}.search[data-v-45d18cae]{margin-left:10px}
|
|
@ -1 +0,0 @@
|
|||
.pagination-container[data-v-6af373ef]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-6af373ef]{display:none}
|
|
@ -0,0 +1 @@
|
|||
.app-container[data-v-7aa35cbf]{position:relative;padding-bottom:60px}.el-table[data-v-7aa35cbf],.filter-container[data-v-7aa35cbf]{padding-bottom:52px}.search[data-v-7aa35cbf]{margin-left:10px}
|
|
@ -1 +1 @@
|
|||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-d6692e38]{position:relative;padding-bottom:60px}.el-table[data-v-d6692e38],.filter-container[data-v-d6692e38]{padding-bottom:52px}
|
||||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-6226c3ab]{position:relative;padding-bottom:60px}.el-table[data-v-6226c3ab],.filter-container[data-v-6226c3ab]{padding-bottom:52px}
|
|
@ -0,0 +1 @@
|
|||
.app-container[data-v-97931c7e]{position:relative;padding-bottom:60px}[data-v-97931c7e] .el-tabs__nav-wrap:after{display:none}.el-table[data-v-97931c7e],.filter-container[data-v-97931c7e]{padding-bottom:5px}[data-v-97931c7e] .el-row{height:50px;line-height:50px}.head_lable[data-v-97931c7e]{font-size:14px;color:#3d3d3d;font-weight:700;margin-right:10px}.scheduling_head[data-v-97931c7e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:28px}.scheduling_tab[data-v-97931c7e]{display:inline-block;padding:12px 28px;font-size:16px;border:1px solid #e2e2e2;border-radius:5px;cursor:pointer}.scheduling_tab+.scheduling_tab[data-v-97931c7e]{margin-left:10px}.active[data-v-97931c7e]{border-color:#1269ff}.el-icon-circle-plus[data-v-97931c7e]{color:#367af0;font-size:16px}.scheduling_tab_pana[data-v-97931c7e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.scheduling_tab_pana .table-list[data-v-97931c7e]{-ms-flex-preferred-size:14.3%;flex-basis:14.3%}.scheduling_tab_pana .table-list .table-list_item[data-v-97931c7e]{height:56px;background:#e4ecff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid #d7ddeb;margin-right:-1px;margin-bottom:-1px}.scheduling_tab_pana .table-list .table-list_item .tit[data-v-97931c7e]{width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly}.scheduling_tab_pana .table-list .table-list-box[data-v-97931c7e]{padding:15px;font-size:14px;color:#fff;border:1px solid #d7ddeb;margin-right:-1px;margin-bottom:-1px}.scheduling_tab_pana .table-list .table-list-box .border[data-v-97931c7e]{padding:13px;background:#367af0;cursor:pointer}
|
|
@ -1 +1 @@
|
|||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-29e14a4a]{position:relative;padding-bottom:60px}.el-table[data-v-29e14a4a],.filter-container[data-v-29e14a4a]{padding-bottom:5px}
|
||||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-4d8f2a06]{position:relative;padding-bottom:60px}.el-table[data-v-4d8f2a06],.filter-container[data-v-4d8f2a06]{padding-bottom:5px}
|
|
@ -0,0 +1 @@
|
|||
@font-face{font-family:PingFang;src:url(../../static/fonts/PingFang\ SC.212ada59.ttf);font-weight:400;font-style:normal}body[data-v-2a6e779e]{font-family:PingFang!important}.infinite-list[data-v-2a6e779e]{list-style-type:none;height:calc(100vh - 154px);padding:0;margin:0}.desc_container+.desc_container[data-v-2a6e779e]{margin-top:10px;padding-top:10px}.desc_container .desc_title[data-v-2a6e779e]{font-size:18px;font-weight:500;color:#333;line-height:25px;margin-bottom:10px}.desc_container .desc_content[data-v-2a6e779e]{font-size:14px;font-weight:300}.problem .problem_form[data-v-2a6e779e]{margin-top:10px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.problem .problem_container[data-v-2a6e779e],.problem .problem_form[data-v-2a6e779e]{display:-webkit-box;display:-ms-flexbox;display:flex}.problem .problem_container .problem_left[data-v-2a6e779e]{font-family:PingFang,sans-serif;font-weight:300;width:18%;background:#fff;padding:0 20px;border-right:2px solid #46a6ff;height:calc(100vh - 154px)}.problem .problem_container .problem_left .btn[data-v-2a6e779e]{color:#fff;padding:10px 20px;cursor:pointer;text-align:center;background:#46a6ff;border-radius:10px}.problem .problem_container .problem_left .btn+.btn[data-v-2a6e779e]{margin-top:10px}.problem .problem_container .problem_right[data-v-2a6e779e]{width:100%;background:#fff;padding:0 20px}.problem .problem_container .problem_right .problem_right_container+.problem_right_container[data-v-2a6e779e]{margin-top:20px}.problem .problem_container .problem_right .problem_right_container .title[data-v-2a6e779e]{font-size:20px;font-weight:600;margin-bottom:10px;color:#46a6ff}.problem .problem_container .problem_right .problem_right_container .title[data-v-2a6e779e]>:first-child{margin-right:40px}.problem .problem_container .problem_right .problem_right_container .desc[data-v-2a6e779e]{font-size:14px;color:#666;line-height:24px}.problem .problem_container .problem_right .problem_right_container .desc .copy-button[data-v-2a6e779e]{margin-left:300px}.image-list-horizontal[data-v-2a6e779e]{display:-webkit-box;display:-ms-flexbox;display:flex;overflow-x:auto;white-space:nowrap;padding:10px 0}.image-card[data-v-2a6e779e]{display:inline-block;width:150px;margin-right:10px}.image-preview[data-v-2a6e779e]{width:100%;height:100px;-o-object-fit:cover;object-fit:cover}.image-preview-full[data-v-2a6e779e]{width:100%;max-height:600px}.image-footer[data-v-2a6e779e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.file-list-horizontal[data-v-2a6e779e]{display:-webkit-box;display:-ms-flexbox;display:flex;overflow-x:auto;white-space:nowrap;padding:10px 0}.file-card[data-v-2a6e779e]{display:inline-block;width:400px;margin-right:10px}.file-info[data-v-2a6e779e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;overflow:hidden}.file-icon[data-v-2a6e779e]{font-size:24px;margin-right:10px}.file-name[data-v-2a6e779e]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:calc(100% - 40px)}.file-footer[data-v-2a6e779e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}
|
|
@ -1 +0,0 @@
|
|||
@font-face{font-family:PingFang;src:url(../../static/fonts/PingFang\ SC.212ada59.ttf);font-weight:400;font-style:normal}body[data-v-d572e954]{font-family:PingFang!important}.infinite-list[data-v-d572e954]{list-style-type:none;height:calc(100vh - 154px);padding:0;margin:0}.desc_container+.desc_container[data-v-d572e954]{margin-top:10px;padding-top:10px}.desc_container .desc_title[data-v-d572e954]{font-size:18px;font-weight:500;color:#333;line-height:25px;margin-bottom:10px}.desc_container .desc_content[data-v-d572e954]{font-size:14px;font-weight:300}.problem .problem_form[data-v-d572e954]{margin-top:10px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.problem .problem_container[data-v-d572e954],.problem .problem_form[data-v-d572e954]{display:-webkit-box;display:-ms-flexbox;display:flex}.problem .problem_container .problem_left[data-v-d572e954]{font-family:PingFang,sans-serif;font-weight:300;width:18%;background:#fff;padding:0 20px;border-right:2px solid #46a6ff;height:calc(100vh - 154px)}.problem .problem_container .problem_left .btn[data-v-d572e954]{color:#fff;padding:10px 20px;cursor:pointer;text-align:center;background:#46a6ff;border-radius:10px}.problem .problem_container .problem_left .btn+.btn[data-v-d572e954]{margin-top:10px}.problem .problem_container .problem_right[data-v-d572e954]{width:100%;background:#fff;padding:0 20px}.problem .problem_container .problem_right .problem_right_container+.problem_right_container[data-v-d572e954]{margin-top:20px}.problem .problem_container .problem_right .problem_right_container .title[data-v-d572e954]{font-size:20px;font-weight:600;margin-bottom:10px;color:#46a6ff}.problem .problem_container .problem_right .problem_right_container .title[data-v-d572e954]>:first-child{margin-right:40px}.problem .problem_container .problem_right .problem_right_container .desc[data-v-d572e954]{font-size:14px;color:#666;line-height:24px}.problem .problem_container .problem_right .problem_right_container .desc .copy-button[data-v-d572e954]{margin-left:300px}.image-list-horizontal[data-v-d572e954]{display:-webkit-box;display:-ms-flexbox;display:flex;overflow-x:auto;white-space:nowrap;padding:10px 0}.image-card[data-v-d572e954]{display:inline-block;width:150px;margin-right:10px}.image-preview[data-v-d572e954]{width:100%;height:100px;-o-object-fit:cover;object-fit:cover}.image-preview-full[data-v-d572e954]{width:100%;max-height:600px}.image-footer[data-v-d572e954]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.file-list-horizontal[data-v-d572e954]{display:-webkit-box;display:-ms-flexbox;display:flex;overflow-x:auto;white-space:nowrap;padding:10px 0}.file-card[data-v-d572e954]{display:inline-block;width:400px;margin-right:10px}.file-info[data-v-d572e954]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;overflow:hidden}.file-icon[data-v-d572e954]{font-size:24px;margin-right:10px}.file-name[data-v-d572e954]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:calc(100% - 40px)}.file-footer[data-v-d572e954]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}
|
|
@ -0,0 +1 @@
|
|||
.pagination-container[data-v-6af373ef]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-6af373ef]{display:none}[data-v-ea07b72c].el-select{width:100%}.addRoutes[data-v-ea07b72c]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:end}
|
|
@ -1 +1 @@
|
|||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-f648eafc]{position:relative;padding-bottom:60px}.el-table[data-v-f648eafc],.filter-container[data-v-f648eafc]{padding-bottom:52px}
|
||||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-79892438]{position:relative;padding-bottom:60px}.el-table[data-v-79892438],.filter-container[data-v-79892438]{padding-bottom:5px}
|
|
@ -1 +1 @@
|
|||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-71de5b60]{position:relative;padding-bottom:60px}.el-table[data-v-71de5b60],.filter-container[data-v-71de5b60]{padding-bottom:52px}
|
||||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-4c275e32]{position:relative;padding-bottom:60px}.el-table[data-v-4c275e32],.filter-container[data-v-4c275e32]{padding-bottom:52px}
|
|
@ -0,0 +1 @@
|
|||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.disable-layer[data-v-25113710]{width:100%;height:100%;background:#f5f7fa;position:absolute;top:0;left:0;z-index:99;opacity:.5}.scrollable-container[data-v-3b4f3a4e]{height:500px}[data-v-3b4f3a4e].el-scrollbar .el-scrollbar__wrap{overflow-x:hidden}.upload-list .wu-yu[data-v-3b4f3a4e],.upload-list[data-v-3b4f3a4e]{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.upload-list .wu-yu[data-v-3b4f3a4e]{margin-bottom:10px;position:relative;width:110px;-ms-flex-line-pack:justify;align-content:space-between}.upload-list .wu-yu+.wu-yu[data-v-3b4f3a4e]{margin-left:5px}.upload-list .wu-yu[data-v-3b4f3a4e] .el-input{width:100px;margin:10px 0 10px 10px}.filter-items[data-v-3b4f3a4e]{position:fixed;left:70%;z-index:66}.img-box[data-v-3b4f3a4e]{position:relative;display:inline-block;width:100px;margin-bottom:10px;margin-left:10px}.img-box+.img-box[data-v-3b4f3a4e]{margin-left:10px}.img-box .desc[data-v-3b4f3a4e]{line-height:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.avatar-uploader[data-v-3b4f3a4e] .el-upload{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-icon-plus[data-v-3b4f3a4e]{display:inline-block;margin-left:10px}.el-icon-folder[data-v-3b4f3a4e]{color:#409eff!important;font-size:100px}.close[data-v-3b4f3a4e]{position:absolute;top:-10px;right:-15px;font-size:18px;color:#409eff}.mistake-content[data-v-3b4f3a4e],.qa-desc[data-v-3b4f3a4e]{display:-webkit-box;display:-ms-flexbox;display:flex}.mistake-left[data-v-3b4f3a4e]{width:90%}.mistake-right[data-v-3b4f3a4e]{padding-left:20px;height:auto;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.app-container[data-v-3b4f3a4e]{position:relative;padding-bottom:60px}.el-table[data-v-3b4f3a4e],.filter-container[data-v-3b4f3a4e]{padding-bottom:52px}.mistake-btn[data-v-3b4f3a4e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;margin-top:10px}.search[data-v-3b4f3a4e]{margin-left:10px}.avatar-uploader .el-upload[data-v-3b4f3a4e]{border:1px solid #131313;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-3b4f3a4e]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-3b4f3a4e]{border:1px solid #979797;border-radius:15px;font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center;position:relative}
|
|
@ -1 +0,0 @@
|
|||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.disable-layer[data-v-25113710]{width:100%;height:100%;background:#f5f7fa;position:absolute;top:0;left:0;z-index:99;opacity:.5}.img-box[data-v-6b889b2e]{position:relative;display:inline-block;width:100px;margin-bottom:10px}.img-box+.img-box[data-v-6b889b2e]{margin-left:10px}.img-box .desc[data-v-6b889b2e]{line-height:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.avatar-uploader[data-v-6b889b2e] .el-upload{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-icon-plus[data-v-6b889b2e]{display:inline-block;margin-left:10px}.el-icon-folder[data-v-6b889b2e]{color:#409eff!important;font-size:100px}.close[data-v-6b889b2e]{position:absolute;top:-10px;right:-8px;font-size:18px;color:#409eff}.mistake-content[data-v-6b889b2e],.qa-desc[data-v-6b889b2e]{display:-webkit-box;display:-ms-flexbox;display:flex}.mistake-left[data-v-6b889b2e]{width:90%}.mistake-right[data-v-6b889b2e]{padding-left:20px;height:auto;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.app-container[data-v-6b889b2e]{position:relative;padding-bottom:60px}.el-table[data-v-6b889b2e],.filter-container[data-v-6b889b2e]{padding-bottom:52px}.mistake-btn[data-v-6b889b2e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;margin-top:10px}.search[data-v-6b889b2e]{margin-left:10px}.avatar-uploader .el-upload[data-v-6b889b2e]{border:1px solid #131313;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-6b889b2e]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-6b889b2e]{border:1px solid #979797;border-radius:15px;font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center;position:relative}
|
|
@ -0,0 +1 @@
|
|||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-78a03c33]{position:relative;padding-bottom:60px}.el-table[data-v-78a03c33],.filter-container[data-v-78a03c33]{padding-bottom:52px}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-24cd5960"],{"5c7c":function(t,e,n){"use strict";n.r(e);var i=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container"},[n("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.listLoading,expression:"listLoading"}],staticStyle:{width:"100%"},attrs:{data:t.list,border:"",fit:"","highlight-current-row":""}},[n("el-table-column",{attrs:{align:"center",label:"ID",width:"60",prop:"id"}}),n("el-table-column",{attrs:{align:"center",label:"姓名",width:"80",prop:"username"}}),n("el-table-column",{attrs:{align:"center",label:"状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[0===e.row.isOnline?n("el-tag",{attrs:{type:"border-card"}},[t._v("下线")]):t._e(),1===e.row.isOnline?n("el-tag",{attrs:{type:"success"}},[t._v("在线")]):t._e(),2===e.row.isOnline?n("el-tag",{attrs:{type:"info"}},[t._v("没上线")]):t._e()]}}])}),n("el-table-column",{attrs:{align:"center",label:"是否分单",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("el-switch",{attrs:{"active-value":1,"inactive-value":0},on:{change:function(n){return t.updateStatus(e.row)}},model:{value:e.row.isEndWork,callback:function(n){t.$set(e.row,"isEndWork",n)},expression:"scope.row.isEndWork"}})]}}])}),n("el-table-column",{attrs:{align:"center",label:"在线时长",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(Math.floor((e.row.data?e.row.data.onlineTime:e.row.onlineTime)/60)||"--")+" 分钟 ")]}}])}),n("el-table-column",{attrs:{width:"138px",align:"center",label:"上线时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(t._f("parseTime")(e.row.start_work_time,"{y}-{m}-{d} {h}:{i}")))])]}}])}),n("el-table-column",{attrs:{width:"138px",align:"center",label:"停止分单时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(t._f("parseTime")(e.row.end_work_time,"{y}-{m}-{d} {h}:{i}")))])]}}])}),n("el-table-column",{attrs:{width:"138px",align:"center",label:"下线时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(t._f("parseTime")(e.row.last_work_time,"{y}-{m}-{d} {h}:{i}")))])]}}])}),n("el-table-column",{attrs:{width:"138px",align:"center",label:"路线"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(t._f("parseTime")(e.row.last_work_time,"{y}-{m}-{d} {h}:{i}")))])]}}])})],1)],1)},a=[],l={name:"GetOnlineList",components:{},data:function(){return{statusArr:{0:"禁用",1:"启用"},list:[],total:0,loading:!1,listLoading:!0,listQuery:{page:1,limit:10,status:null,content:""},dialogCreate:!1,dialogEdit:!1,item:{},anchors:{}}},created:function(){this.listQuery.status=this.$route.query.status||null,this.listQuery.content=this.$route.query.content||null,this.getOnlineList()},methods:{getOnlineList:function(){var t=this;this.listLoading=!0,this.$axios.get("/admin/admin/getOnlineList",{params:this.listQuery}).then((function(e){t.list=e.data,t.listLoading=!1})).catch((function(){t.listLoading=!1}))},updateStatus:function(t){var e=this;this.$axios.post("/admin/admin/editInfo",{id:t.id,order_num:t.order_num,is_order:t.isEndWork}).then((function(){e.getOnlineList()})).catch((function(){}))}}},s=l,r=(n("89a4"),n("2877")),o=Object(r["a"])(s,i,a,!1,null,"45d18cae",null);e["default"]=o.exports},"89a4":function(t,e,n){"use strict";n("d4f7")},d4f7:function(t,e,n){}}]);
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-27f79c9e"],{"5c7c":function(t,e,n){"use strict";n.r(e);var i=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container"},[n("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.listLoading,expression:"listLoading"}],staticStyle:{width:"100%"},attrs:{data:t.list,border:"",fit:"","highlight-current-row":""}},[n("el-table-column",{attrs:{align:"center",label:"ID",width:"60",prop:"id"}}),n("el-table-column",{attrs:{align:"center",label:"姓名",width:"80",prop:"username"}}),n("el-table-column",{attrs:{align:"center",label:"状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[0===e.row.isOnline?n("el-tag",{attrs:{type:"border-card"}},[t._v("下线")]):t._e(),1===e.row.isOnline?n("el-tag",{attrs:{type:"success"}},[t._v("在线")]):t._e(),2===e.row.isOnline?n("el-tag",{attrs:{type:"info"}},[t._v("没上线")]):t._e()]}}])}),n("el-table-column",{attrs:{align:"center",label:"是否分单",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("el-switch",{attrs:{"active-value":1,"inactive-value":0},on:{change:function(n){return t.updateStatus(e.row)}},model:{value:e.row.isEndWork,callback:function(n){t.$set(e.row,"isEndWork",n)},expression:"scope.row.isEndWork"}})]}}])}),n("el-table-column",{attrs:{align:"center",label:"在线时长",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(Math.floor((e.row.data?e.row.data.onlineTime:e.row.onlineTime)/60)||"--")+" 分钟 ")]}}])}),n("el-table-column",{attrs:{width:"138px",align:"center",label:"上线时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(t._f("parseTime")(e.row.start_work_time,"{y}-{m}-{d} {h}:{i}")))])]}}])}),n("el-table-column",{attrs:{width:"138px",align:"center",label:"停止分单时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(t._f("parseTime")(e.row.end_work_time,"{y}-{m}-{d} {h}:{i}")))])]}}])}),n("el-table-column",{attrs:{width:"138px",align:"center",label:"下线时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(t._f("parseTime")(e.row.last_work_time,"{y}-{m}-{d} {h}:{i}")))])]}}])})],1)],1)},a=[],l={name:"GetOnlineList",components:{},data:function(){return{statusArr:{0:"禁用",1:"启用"},list:[],total:0,loading:!1,listLoading:!0,listQuery:{page:1,limit:10,status:null,content:""},dialogCreate:!1,dialogEdit:!1,item:{},anchors:{}}},created:function(){this.listQuery.status=this.$route.query.status||null,this.listQuery.content=this.$route.query.content||null,this.getOnlineList()},methods:{getOnlineList:function(){var t=this;this.listLoading=!0,this.$axios.get("/admin/admin/getOnlineList",{params:this.listQuery}).then((function(e){t.list=e.data,t.listLoading=!1})).catch((function(){t.listLoading=!1}))},updateStatus:function(t){var e=this;this.$axios.post("/admin/admin/editInfo",{id:t.id,order_num:t.order_num,is_order:t.isEndWork}).then((function(){e.getOnlineList()})).catch((function(){}))}}},s=l,o=(n("d8ad"),n("2877")),r=Object(o["a"])(s,i,a,!1,null,"7aa35cbf",null);e["default"]=r.exports},a659:function(t,e,n){},d8ad:function(t,e,n){"use strict";n("a659")}}]);
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0de3a1"],{"856d":function(t,e,l){"use strict";l.r(e);var i=function(){var t=this,e=t.$createElement,l=t._self._c||e;return l("div",{staticClass:"app-container"},[l("div",{staticClass:"filter-container"},[l("el-date-picker",{staticClass:"filter-item",attrs:{type:"datetimerange","default-time":["00:00:00","23:59:59"],"range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期"},model:{value:t.listQuery.times,callback:function(e){t.$set(t.listQuery,"times",e)},expression:"listQuery.times"}}),l("el-select",{staticClass:"filter-item",attrs:{filterable:"",placeholder:"请选择"},model:{value:t.listQuery.os,callback:function(e){t.$set(t.listQuery,"os",e)},expression:"listQuery.os"}},[l("el-option",{key:"",attrs:{label:"请选择",value:""}}),t._l(t.oss,(function(t,e){return l("el-option",{key:e,attrs:{label:t,value:e}})}))],2),l("el-button",{staticClass:"filter-item",attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.getList}},[t._v(" 搜索 ")])],1),l("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.listLoading,expression:"listLoading"}],staticStyle:{width:"100%"},attrs:{data:t.list,border:"",fit:"","highlight-current-row":""}},[l("el-table-column",{attrs:{align:"center",fixed:"",label:"姓名",width:"120",prop:"admin.name"}}),l("el-table-column",{attrs:{align:"center",label:"订单数",width:"120",prop:"orders"}}),l("el-table-column",{attrs:{align:"center",label:"订单总金额"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.total_price/100)+" ")]}}])}),l("el-table-column",{attrs:{align:"center",label:"核销数",width:"120",prop:"assets"}}),l("el-table-column",{attrs:{align:"center",label:"核销金额"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.asset_price/100)+" ")]}}])}),l("el-table-column",{attrs:{align:"center",label:"未付款订单",width:"120",prop:"nopays"}}),l("el-table-column",{attrs:{align:"center",label:"未付款金额"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.nopay_price/100)+" ")]}}])}),l("el-table-column",{attrs:{align:"center",label:"核销率(按订单)",width:"160"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.write_rate)+"% ")]}}])}),l("el-table-column",{attrs:{align:"center",width:"180",label:"核销率(按销售额)"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.write_rate_price)+"% ")]}}])})],1)],1)},a=[],s=l("5530"),n=(l("a15b"),l("d81d"),l("b64b"),{name:"Datalist",data:function(){return{oss:null,list:[],listLoading:!0,listQuery:{}}},created:function(){this.getList()},methods:{getList:function(t){var e=this;if(1!=t)this.listQuery.excel=0,this.$axios.get("/admin/data/index",{params:this.listQuery}).then((function(t){e.list=t.data,e.oss=t.ext.oss,e.listLoading=!1}));else{if(this.listQuery.excel=1,!this.listQuery.times)return void this.$message({message:"请选择日期",type:"warning"});var l=this.listQuery.times[0]instanceof Date,i=Object(s["a"])(Object(s["a"])({},this.listQuery),{},{times:[l?this.listQuery.times[0].toISOString():"",l?this.listQuery.times[1].toISOString():""]});window.open("/admin/data/index?"+this.objectToQuery(i))}},objectToQuery:function(t){return Object.keys(t).map((function(e){var l=t[e];return void 0==l||null==l?"":encodeURIComponent(e)+"="+encodeURIComponent(l)})).join("&")}}}),r=n,o=l("2877"),c=Object(o["a"])(r,i,a,!1,null,null,null);e["default"]=c.exports}}]);
|
|
@ -0,0 +1 @@
|
|||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0de3a1"],{"856d":function(t,e,l){"use strict";l.r(e);var i=function(){var t=this,e=t.$createElement,l=t._self._c||e;return l("div",{staticClass:"app-container"},[l("div",{staticClass:"filter-container"},[l("el-date-picker",{staticClass:"filter-item",attrs:{type:"datetimerange","default-time":["00:00:00","23:59:59"],"range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期"},model:{value:t.listQuery.times,callback:function(e){t.$set(t.listQuery,"times",e)},expression:"listQuery.times"}}),l("el-select",{staticClass:"filter-item",attrs:{filterable:"",placeholder:"请选择"},model:{value:t.listQuery.os,callback:function(e){t.$set(t.listQuery,"os",e)},expression:"listQuery.os"}},[l("el-option",{key:"",attrs:{label:"请选择",value:""}}),t._l(t.oss,(function(t,e){return l("el-option",{key:e,attrs:{label:t,value:e}})}))],2),l("el-button",{staticClass:"filter-item",attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.getList}},[t._v(" 搜索 ")]),l("el-button",{staticClass:"filter-item",attrs:{type:"primary",icon:"el-icon-search"},on:{click:function(e){return t.getList(1)}}},[t._v(" 导出 ")])],1),l("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.listLoading,expression:"listLoading"}],staticStyle:{width:"100%"},attrs:{data:t.list,border:"",fit:"","highlight-current-row":""}},[l("el-table-column",{attrs:{align:"center",fixed:"",label:"姓名",width:"120",prop:"admin.name"}}),l("el-table-column",{attrs:{align:"center",label:"订单数",width:"120",prop:"orders"}}),l("el-table-column",{attrs:{align:"center",label:"订单总金额"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.total_price/100)+" ")]}}])}),l("el-table-column",{attrs:{align:"center",label:"核销数",width:"120",prop:"assets"}}),l("el-table-column",{attrs:{align:"center",label:"核销金额"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.asset_price/100)+" ")]}}])}),l("el-table-column",{attrs:{align:"center",label:"未付款订单",width:"120",prop:"nopays"}}),l("el-table-column",{attrs:{align:"center",label:"未付款金额"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.nopay_price/100)+" ")]}}])}),l("el-table-column",{attrs:{align:"center",label:"核销率(按订单)",width:"160"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.write_rate)+"% ")]}}])}),l("el-table-column",{attrs:{align:"center",width:"180",label:"核销率(按销售额)"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.write_rate_price)+"% ")]}}])}),l("el-table-column",{attrs:{align:"center",label:"当月核销率(按订单)",width:"160"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.month_write_rate)+"% ")]}}])}),l("el-table-column",{attrs:{align:"center",width:"180",label:"当月核销率(按销售额)"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(e.row.month_write_rate_price)+"% ")]}}])})],1)],1)},a=[],n=l("5530"),s=(l("a15b"),l("d81d"),l("b64b"),{name:"Datalist",data:function(){return{oss:null,list:[],listLoading:!0,listQuery:{}}},created:function(){this.getList()},methods:{getList:function(t){var e=this;if(1!=t)this.listQuery.excel=0,this.$axios.get("/admin/data/index",{params:this.listQuery}).then((function(t){e.list=t.data,e.oss=t.ext.oss,e.listLoading=!1}));else{if(this.listQuery.excel=1,!this.listQuery.times)return void this.$message({message:"请选择日期",type:"warning"});var l=this.listQuery.times[0]instanceof Date,i=Object(n["a"])(Object(n["a"])({},this.listQuery),{},{times:[l?this.listQuery.times[0].toISOString():"",l?this.listQuery.times[1].toISOString():""]});window.open("/admin/data/index?"+this.objectToQuery(i))}},objectToQuery:function(t){return Object.keys(t).map((function(e){var l=t[e];return void 0==l||null==l?"":encodeURIComponent(e)+"="+encodeURIComponent(l)})).join("&")}}}),r=s,o=l("2877"),c=Object(o["a"])(r,i,a,!1,null,null,null);e["default"]=c.exports}}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1,28 @@
|
|||
{"status":200,"message":"\u6210\u529f","data":{"sellerOrderResultDOS":[],"showVerify":false,"page":{"currentPage":2,"pageSize":10,"totalCount":0,"noMore":false},"canVerify":false,"canBatchDelivery":false,"canAsyncExportOrder":true},"success":true,"traceId":"EL2AgICAsoKsChjdAyDS_sLB8TEowpfGgQ8="}
|
||||
|
||||
ALTER TABLE `travel`.`orders`
|
||||
MODIFY COLUMN `appointment_status` tinyint NOT NULL DEFAULT 0 COMMENT '是否已预约(0-未预约,1-已预约未处理,2-已预约已处理)' AFTER `next_remind_time`;
|
||||
|
||||
ALTER TABLE `admins`
|
||||
ADD COLUMN `type` tinyint NOT NULL DEFAULT 0 COMMENT '类型(0-管理员,1-客服,2-主播,3-中控)' AFTER `route_type`;
|
||||
|
||||
ALTER TABLE `orders`
|
||||
ADD COLUMN `live_room_works` int NOT NULL DEFAULT 0 COMMENT '排班表id' AFTER `appointment_status`;
|
||||
|
||||
|
||||
|
||||
2024/10/13
|
||||
ALTER TABLE `orders`
|
||||
ADD COLUMN `is_refunded` tinyint NOT NULL DEFAULT 0 COMMENT '是否已退款(0-否,1-是)' AFTER `update_time`;
|
||||
|
||||
update
|
||||
`orders`
|
||||
set is_refunded = 1
|
||||
WHERE
|
||||
`os` IN ( '1', '7' ) and order_status = 5;
|
||||
|
||||
update
|
||||
`orders`
|
||||
set is_refunded = 1
|
||||
WHERE
|
||||
(`os` IN ( '3', '5' ) and order_status = 4)
|
|
@ -526,3 +526,35 @@ function input(string $param = null, $default = null)
|
|||
{
|
||||
return is_null($param) ? request()->all() : request()->input($param, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一周的日期
|
||||
* @return array
|
||||
*/
|
||||
function get_week_days() {
|
||||
// 获取当前日期
|
||||
$today = new DateTime();
|
||||
|
||||
// 获取当前周的周一
|
||||
$monday = clone $today;
|
||||
$monday->modify('monday this week');
|
||||
|
||||
// 获取当前周的周日
|
||||
$sunday = clone $today;
|
||||
$sunday->modify('sunday this week');
|
||||
|
||||
// 初始化日期数组
|
||||
$dates = [];
|
||||
$dayNmae = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期天'];
|
||||
// 从周一到周日循环
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
$date = clone $monday;
|
||||
$date->modify("+$i days");
|
||||
$dates[] = [
|
||||
'date_name' => $dayNmae[$i],
|
||||
'date' => $date->format('Y-m-d')
|
||||
];
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue