fix;合同管理

This commit is contained in:
tt 2024-10-21 19:21:39 +08:00
parent e937d047ce
commit dc77f2c6a8
7 changed files with 1048 additions and 270 deletions

View File

@ -1,16 +1,55 @@
import request from '@/utils/request' import request from "@/utils/request";
// 获取路线列表 // 获取路线列表
export function getProductsList() { export function getProductsList() {
return request({ return request({
url: 'admin/products/list', url: "admin/products/list",
method: 'get' method: "get",
}) });
} }
export function addProducts(data) { export function addProducts(data) {
return request({ return request({
url: '/admin/products/add', url: "/admin/products/add",
method: 'post', method: "post",
data data,
}) });
}
// 获取商品列表
export function getProductsListsApi(data) {
return request({
url: `/admin/products/list?page=${data.page}&limit=${data.limit}`,
method: "get",
});
}
// 添加商品
export function postAddProductApi(data) {
return request({
url: `/admin/products/add`,
method: "post",
data,
});
}
// 商品排期详情
export function getProductSchedules(data) {
return request({
url: `/admin/products/productschedules?id=${data}`,
method: "get",
});
}
// 添加商品排期
export function addProductSchedulesApi(data) {
return request({
url: `/admin/products/addproductschedules`,
method: "post",
data,
});
}
// 合同管理列表
export function getContractListsApi() {
return request({
url: `/admin/setting/getContractSetting`,
method: "get",
});
} }

View File

@ -7,7 +7,10 @@
!item.alwaysShow !item.alwaysShow
" "
> >
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> <app-link
v-if="onlyOneChild.meta && !onlyOneChild.meta.hidden"
:to="resolvePath(onlyOneChild.path)"
>
<el-menu-item <el-menu-item
:index="resolvePath(onlyOneChild.path)" :index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }" :class="{ 'submenu-title-noDropdown': !isNest }"

View File

@ -1,10 +1,10 @@
import Vue from 'vue' import Vue from "vue";
import Router from 'vue-router' import Router from "vue-router";
Vue.use(Router) Vue.use(Router);
/* Layout */ /* Layout */
import Layout from '@/layout' import Layout from "@/layout";
/** /**
* constantRoutes * constantRoutes
@ -13,60 +13,60 @@ import Layout from '@/layout'
*/ */
export const constantRoutes = [ export const constantRoutes = [
{ {
path: '/redirect', path: "/redirect",
component: Layout, component: Layout,
hidden: true, hidden: true,
children: [ children: [
{ {
path: '/redirect/:path(.*)', path: "/redirect/:path(.*)",
component: () => import('@/views/redirect/index') component: () => import("@/views/redirect/index"),
} },
] ],
}, },
{ {
path: '/login', path: "/login",
component: () => import('@/views/login/index'), component: () => import("@/views/login/index"),
hidden: true hidden: true,
}, },
{ {
path: '/home', path: "/home",
component: () => import('@/views/home/index'), component: () => import("@/views/home/index"),
hidden: true hidden: true,
}, },
{ {
path: '/line_on_sale', path: "/line_on_sale",
component: () => import('@/views/home/line_on_sale'), component: () => import("@/views/home/line_on_sale"),
hidden: true hidden: true,
}, },
{ {
path: '/auth-redirect', path: "/auth-redirect",
component: () => import('@/views/login/auth-redirect'), component: () => import("@/views/login/auth-redirect"),
hidden: true hidden: true,
}, },
{ {
path: '/404', path: "/404",
component: () => import('@/views/error-page/404'), component: () => import("@/views/error-page/404"),
hidden: true hidden: true,
}, },
{ {
path: '/401', path: "/401",
component: () => import('@/views/error-page/401'), component: () => import("@/views/error-page/401"),
hidden: true hidden: true,
}, },
{ {
path: '/', path: "/",
component: Layout, component: Layout,
redirect: '/dashboard', redirect: "/dashboard",
children: [ children: [
{ {
path: 'dashboard', path: "dashboard",
component: () => import('@/views/dashboard/index'), component: () => import("@/views/dashboard/index"),
name: 'Dashboard', name: "Dashboard",
meta: { title: '系统面板', icon: 'dashboard', affix: true } meta: { title: "系统面板", icon: "dashboard", affix: true },
} },
] ],
} },
] ];
/** /**
* asyncRoutes * asyncRoutes
@ -74,53 +74,82 @@ export const constantRoutes = [
*/ */
export const asyncRoutes = [ export const asyncRoutes = [
{ {
path: '/system', path: "/system",
component: Layout, component: Layout,
redirect: '/system', redirect: "/system",
alwaysShow: true, alwaysShow: true,
name: 'System', name: "System",
meta: { meta: {
title: '系统管理', title: "系统管理",
icon: 'el-icon-s-home', icon: "el-icon-s-home",
roles: ['admin'] roles: ["admin"],
}, },
children: [ children: [
{ {
path: 'admin', path: "admin",
component: () => import('@/views/admin/index'), component: () => import("@/views/admin/index"),
name: 'Admin', name: "Admin",
meta: { meta: {
title: '管理员', title: "管理员",
roles: ['admin'] roles: ["admin"],
} },
}, },
{ {
path: 'works', path: "works",
component: () => import('@/views/admin/works'), component: () => import("@/views/admin/works"),
name: 'Works', name: "Works",
meta: { meta: {
title: '排班表', title: "排班表",
roles: ['admin'] roles: ["admin"],
} },
}, },
{ {
path: 'scheduling', path: "scheduling",
component: () => import('@/views/scheduling/index'), component: () => import("@/views/scheduling/index"),
name: 'scheduling', name: "scheduling",
meta: { meta: {
title: '直播排班', title: "直播排班",
roles: ['admin'] roles: ["admin"],
} },
}, },
{ {
path: 'onlines', path: "onlines",
component: () => import('@/views/onlines/online.vue'), component: () => import("@/views/onlines/online.vue"),
name: 'onlines', name: "onlines",
meta: { meta: {
title: '在线客服', title: "在线客服",
roles: ['admin'] roles: ["admin"],
} },
}/*, },
{
path: "proManagement",
component: () => import("@/views/proManagement/index"),
name: "ProManagement",
meta: {
title: "商品管理",
roles: ["admin"],
},
},
{
path: "contract",
component: () => import("@/views/proManagement/contract"),
name: "ProManagementContract",
meta: {
title: "合同管理",
roles: ["admin"],
},
},
{
path: "proScheduling",
component: () => import("@/views/proManagement/scheduling"),
name: "ProScheduling",
meta: {
title: "排班管理",
roles: ["admin"],
hidden: true,
},
},
/*,
{ {
path: 'shortcut', path: 'shortcut',
component: () => import('@/views/shortcut/shortcutContent.vue'), component: () => import('@/views/shortcut/shortcutContent.vue'),
@ -139,28 +168,28 @@ export const asyncRoutes = [
roles: ['admin'] roles: ['admin']
} }
}*/ }*/
] ],
}, },
{ {
path: '/order', path: "/order",
component: Layout, component: Layout,
redirect: '/order/index', redirect: "/order/index",
alwaysShow: true, alwaysShow: true,
name: 'Orders', name: "Orders",
meta: { meta: {
title: '订单管理', title: "订单管理",
icon: 'money', icon: "money",
roles: ['order_index', 'editor'] roles: ["order_index", "editor"],
}, },
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/order/index'), component: () => import("@/views/order/index"),
name: 'OrderList', name: "OrderList",
meta: { meta: {
title: '订单列表', title: "订单列表",
roles: ['order_pub', 'editor'] roles: ["order_pub", "editor"],
} },
}, },
/* { /* {
path: 'pub', path: 'pub',
@ -172,122 +201,122 @@ export const asyncRoutes = [
} }
},*/ },*/
{ {
path: 'back', path: "back",
component: () => import('@/views/order/back'), component: () => import("@/views/order/back"),
name: 'OrderBack', name: "OrderBack",
meta: { meta: {
title: '流转订单', title: "流转订单",
roles: ['order_back', 'editor'] roles: ["order_back", "editor"],
} },
}, },
{ {
path: 'abandoned', path: "abandoned",
component: () => import('@/views/order/abandoned'), component: () => import("@/views/order/abandoned"),
name: 'OrderBack', name: "OrderBack",
meta: { meta: {
title: '已放弃订单', title: "已放弃订单",
roles: ['order_back', 'editor'] roles: ["order_back", "editor"],
} },
}, },
{ {
path: 'used', path: "used",
component: () => import('@/views/order/used'), component: () => import("@/views/order/used"),
name: 'OrderBack', name: "OrderBack",
meta: { meta: {
title: '已使用订单', title: "已使用订单",
roles: ['order_back', 'editor'] roles: ["order_back", "editor"],
} },
}, },
{ {
path: 'refunded', path: "refunded",
component: () => import('@/views/order/refunded'), component: () => import("@/views/order/refunded"),
name: 'OrderBack', name: "OrderBack",
meta: { meta: {
title: '已退款订单', title: "已退款订单",
roles: ['order_back', 'editor'] roles: ["order_back", "editor"],
} },
}, },
{ {
path: 'appointment', path: "appointment",
component: () => import('@/views/order/appointment'), component: () => import("@/views/order/appointment"),
name: 'appointment', name: "appointment",
meta: { meta: {
title: '预约记录', title: "预约记录",
roles: ['order_back', 'editor'] roles: ["order_back", "editor"],
} },
}, },
] ],
}, },
{ {
path: '/qa', path: "/qa",
component: Layout, component: Layout,
redirect: '/qa/qa', redirect: "/qa/qa",
alwaysShow: true, alwaysShow: true,
name: 'Qa', name: "Qa",
meta: { meta: {
title: 'QA管理', title: "QA管理",
icon: 'el-icon-question', icon: "el-icon-question",
roles: ['order_index', 'editor', 'franchisee'] roles: ["order_index", "editor", "franchisee"],
}, },
children: [ children: [
{ {
path: 'problem', path: "problem",
component: () => import('@/views/qa/problem.vue'), component: () => import("@/views/qa/problem.vue"),
name: 'problem', name: "problem",
meta: { meta: {
title: 'QA常见问题', title: "QA常见问题",
roles: ['order_pub', 'editor', 'franchisee'] roles: ["order_pub", "editor", "franchisee"],
} },
}, },
{ {
path: 'qa', path: "qa",
component: () => import('@/views/qa/qa.vue'), component: () => import("@/views/qa/qa.vue"),
name: 'qa', name: "qa",
meta: { meta: {
title: 'QA管理列表', title: "QA管理列表",
roles: ['admin'] roles: ["admin"],
} },
}, },
{ {
path: 'city', path: "city",
component: () => import('@/views/qa/city.vue'), component: () => import("@/views/qa/city.vue"),
name: 'city', name: "city",
meta: { meta: {
title: '城市管理列表', title: "城市管理列表",
roles: ['admin'] roles: ["admin"],
} },
} },
] ],
}, },
{ {
path: '/data', path: "/data",
component: Layout, component: Layout,
redirect: '/data/index', redirect: "/data/index",
alwaysShow: true, alwaysShow: true,
name: 'Data', name: "Data",
meta: { meta: {
title: '数据统计', title: "数据统计",
icon: 'chart', icon: "chart",
roles: ['data_index'] roles: ["data_index"],
}, },
children: [ children: [
{ {
path: 'product', path: "product",
component: () => import('@/views/order/product'), component: () => import("@/views/order/product"),
name: 'productNameList', name: "productNameList",
meta: { meta: {
title: '产品统计', title: "产品统计",
roles: ['order_pub', 'editor'] roles: ["order_pub", "editor"],
} },
}, },
{ {
path: 'index', path: "index",
component: () => import('@/views/data/index'), component: () => import("@/views/data/index"),
name: 'Index', name: "Index",
meta: { meta: {
title: '跟进统计', title: "跟进统计",
roles: ['data_index'] roles: ["data_index"],
} },
}, },
/* { /* {
path: 'online', path: 'online',
@ -308,98 +337,100 @@ export const asyncRoutes = [
} }
},*/ },*/
{ {
path: 'sale', path: "sale",
component: () => import('@/views/data/sale'), component: () => import("@/views/data/sale"),
name: 'Sale', name: "Sale",
meta: { meta: {
title: '销售统计', title: "销售统计",
roles: ['data_sale'] roles: ["data_sale"],
} },
} },
] ],
}, { },
path: '/log', {
path: "/log",
component: Layout, component: Layout,
redirect: '/log/index', redirect: "/log/index",
alwaysShow: true, alwaysShow: true,
name: 'Log', name: "Log",
meta: { meta: {
title: '日志记录', title: "日志记录",
icon: 'nested', icon: "nested",
roles: ['follow_index', 'log_index', 'editor', 'franchisee'] roles: ["follow_index", "log_index", "editor", "franchisee"],
}, },
children: [ children: [
{ {
path: 'follow', path: "follow",
component: () => import('@/views/log/follow'), component: () => import("@/views/log/follow"),
name: 'Follow', name: "Follow",
meta: { meta: {
title: '跟进记录', title: "跟进记录",
roles: ['follow_index', 'editor', 'franchisee'] roles: ["follow_index", "editor", "franchisee"],
} },
}, },
{ {
path: 'index', path: "index",
component: () => import('@/views/log/index'), component: () => import("@/views/log/index"),
name: 'LogIndex', name: "LogIndex",
meta: { meta: {
title: '日志记录', title: "日志记录",
roles: ['log_index'] roles: ["log_index"],
} },
} },
] ],
}, },
{ {
path: '/announcements', path: "/announcements",
component: Layout, component: Layout,
redirect: '/announcements/index', redirect: "/announcements/index",
alwaysShow: true, alwaysShow: true,
name: 'announcements', name: "announcements",
meta: { meta: {
title: '公告管理', title: "公告管理",
icon: 'el-icon-s-promotion', icon: "el-icon-s-promotion",
roles: ['admin'] roles: ["admin"],
}, },
children: [ children: [
{ {
path: 'list', path: "list",
component: () => import('@/views/announcements/list'), component: () => import("@/views/announcements/list"),
name: 'list', name: "list",
meta: { meta: {
title: '公告列表', title: "公告列表",
roles: ['admin'] roles: ["admin"],
} },
} },
] ],
}, },
{ {
path: '/icon', path: "/icon",
component: Layout, component: Layout,
children: [ children: [
{ {
path: 'index', path: "index",
component: () => import('@/views/icons/index'), component: () => import("@/views/icons/index"),
name: 'Icons', name: "Icons",
meta: { title: 'Icons', icon: 'icon', noCache: true } meta: { title: "Icons", icon: "icon", noCache: true },
} },
] ],
}, },
// 404 page must be placed at the end !!! // 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true } { path: "*", redirect: "/404", hidden: true },
] ];
const createRouter = () => new Router({ const createRouter = () =>
// mode: 'history', // require service support new Router({
scrollBehavior: () => ({ y: 0 }), // mode: 'history', // require service support
routes: constantRoutes scrollBehavior: () => ({ y: 0 }),
}) routes: constantRoutes,
});
const router = createRouter() const router = createRouter();
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() { export function resetRouter() {
const newRouter = createRouter() const newRouter = createRouter();
router.matcher = newRouter.matcher // reset router router.matcher = newRouter.matcher; // reset router
} }
export default router export default router;

View File

@ -1,87 +1,89 @@
import axios from 'axios' import axios from "axios";
import { MessageBox, Message } from 'element-ui' import { MessageBox, Message } from "element-ui";
import store from '@/store' import store from "@/store";
import { getToken,removeToken } from '@/utils/auth' import { getToken, removeToken } from "@/utils/auth";
// create an axios instance
const service = axios.create({ const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests // withCredentials: true, // send cookies when cross-domain requests
timeout: 10000 // request timeout timeout: 10000, // request timeout
}) });
// request interceptor // request interceptor
service.interceptors.request.use( service.interceptors.request.use(
config => { (config) => {
// do something before request is sent // do something before request is sent
if (store.getters.token) { if (store.getters.token) {
// let each request carry token // let each request carry token
// ['X-Token'] is a custom headers key // ['X-Token'] is a custom headers key
// please modify it according to the actual situation // please modify it according to the actual situation
config.headers['X-Token'] = getToken() config.headers["X-Token"] = getToken();
} }
return config return config;
}, },
error => { (error) => {
// do something with request error // do something with request error
return Promise.reject(error) return Promise.reject(error);
} }
) );
// response interceptor // response interceptor
service.interceptors.response.use( service.interceptors.response.use(
/** /**
* If you want to get http information such as headers or status * If you want to get http information such as headers or status
* Please return response => response * Please return response => response
*/ */
/** /**
* Determine the request status by custom code * Determine the request status by custom code
* Here is just an example * Here is just an example
* You can also judge the status by HTTP Status Code * You can also judge the status by HTTP Status Code
*/ */
response => { (response) => {
const res = response.data const res = response.data;
// if the custom code is not 20000, it is judged as an error. // if the custom code is not 20000, it is judged as an error.
if (res.error !== 0) { if (res.error !== 0) {
Message({ Message({
message: res.msg || 'Error', message: res.msg || "Error",
type: 'error', type: "error",
duration: 5 * 1000 duration: 5 * 1000,
}) });
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) { if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login // to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { MessageBox.confirm(
confirmButtonText: 'Re-Login', "You have been logged out, you can cancel to stay on this page, or log in again",
cancelButtonText: 'Cancel', "Confirm logout",
type: 'warning' {
}).then(() => { confirmButtonText: "Re-Login",
store.dispatch('user/resetToken').then(() => { cancelButtonText: "Cancel",
location.reload() type: "warning",
}) }
}) ).then(() => {
store.dispatch("user/resetToken").then(() => {
location.reload();
});
});
} }
if (res.code == 400) { if (res.code == 400) {
removeToken() removeToken();
} }
return Promise.reject(new Error(res.message || 'Error')) return Promise.reject(new Error(res.message || "Error"));
} else { } else {
return res return res;
} }
}, },
error => { (error) => {
console.log('err' + error) // for debug console.log("err" + error); // for debug
Message({ Message({
message: error.message, message: error.message,
type: 'error', type: "error",
duration: 5 * 1000 duration: 5 * 1000,
}) });
return Promise.reject(error) return Promise.reject(error);
} }
) );
export default service export default service;

View File

@ -0,0 +1,62 @@
<template>
<div class="contract">
<div class="lay">
<div class="flex">
<el-button type="primary">境内合同</el-button>
<div class="word" v-if="contractObj.domestic">
文件:{{ contractObj.domestic }}
</div>
<el-button type="success" v-if="contractObj.domestic"
>下载境内合同</el-button
>
</div>
<div class="flex">
<el-button type="danger">境外合同</el-button>
<div class="word" v-if="contractObj.abroad">
文件:{{ contractObj.abroad }}
</div>
<el-button type="success" v-if="contractObj.abroad"
>下载境外合同</el-button
>
</div>
</div>
</div>
</template>
<script>
import { getContractListsApi } from "@/api/admin";
export default {
data() {
return {
contractObj: {},
};
},
created() {
this.getList();
},
methods: {
async getList() {
const res = await getContractListsApi();
this.contractObj = res.data;
},
onSubmit() {},
},
};
</script>
<style scoped lang="scss">
.contract {
padding-top: 20px;
.lay {
width: 100%;
.flex {
display: flex;
align-items: center;
padding-left: 20px;
margin-bottom: 20px;
}
.word {
margin: 0 50px;
}
}
}
</style>

View File

@ -0,0 +1,463 @@
<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-button>
<el-button
class="filter-item"
style="margin-left: 10px"
type="primary"
icon="el-icon-circle-plus"
@click="handleCreate('add', [])"
>
新增商品
</el-button>
</div>
<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('edit', scope.row)"
size="small"
icon="el-icon-edit"
>
修改
</el-button>
<el-button
type="primary"
v-if="scope.row.status"
@click="onWork(scope.row.id)"
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="商品名称"
width=""
prop="product_name"
></el-table-column>
<el-table-column
align="center"
label="所属平台"
width="160"
prop="os"
></el-table-column>
<el-table-column align="type" label="境内/境外" width="100" prop="type">
<template slot-scope="scope">
<span>{{ scope.row.type === 1 ? "境内" : "境外" }}</span>
</template>
</el-table-column>
<el-table-column
align="type"
label="天数"
width="60"
prop="day"
></el-table-column>
<el-table-column
align="type"
label="天数"
width="60"
prop="night"
></el-table-column>
<el-table-column
align="type"
label="详细行程"
width="180"
prop="trip_info"
>
<template slot-scope="scope">
<span class="link-type" @click="openLink(scope.row.trip_info)">{{
scope.row.trip_info
}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="创建时间">
<template slot-scope="scope">
<span>{{
scope.row.created_at | 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.updated_at | parseTime("{y}-{m}-{d} {h}:{i}")
}}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList"
/>
<!-- 添加/编辑表单 -->
<el-dialog
:visible.sync="dialogVisible"
@open="onOpen"
@close="onClose"
:title="dialogTitle"
>
<el-form
ref="elForm"
:model="formData"
:rules="rules"
size="medium"
label-width="120px"
>
<el-form-item label="请选择平台" prop="os">
<el-select v-model="formData.os" placeholder="请选择平台" clearable>
<el-option
v-for="(item, index) in ossArr"
:key="item.id"
:label="item.os"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="产品名称" prop="product_name">
<el-input
v-model="formData.product_name"
placeholder="请输入产品名称"
clearable
>
</el-input>
</el-form-item>
<el-form-item label="第三方产品编号" prop="third_product_id">
<el-input
v-model="formData.third_product_id"
placeholder="请输入第三方产品编号"
clearable
>
</el-input>
</el-form-item>
<el-form-item label="天数" prop="day">
<el-input v-model="formData.day" placeholder="请输入天数" clearable>
</el-input>
</el-form-item>
<el-form-item label="晚数" prop="night">
<el-input v-model="formData.night" placeholder="请输入晚数" clearable>
</el-input>
</el-form-item>
<el-form-item label="出游类型" prop="type">
<el-radio-group v-model="formData.type" size="medium">
<el-radio
v-for="(item, index) in fieldTypeOptions"
:key="item.value"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item label="上传" prop="trip_info" required>
<el-upload
ref="field105"
action=""
:before-upload="wordBeforeUpload"
:http-request="handlesAvatarSuccess"
:on-success="handleWordSuccess"
:on-error="handleUploadError"
:on-remove="handleRemove"
:on-change="handleChange"
:before-remove="beforeRemove"
:limit="1"
:file-list="fieldFileList"
accept=".pdf,.docx,.xlsx"
>
<el-button size="small" type="primary" icon="el-icon-upload"
>点击上传</el-button
>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="handelConfirm">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import Pagination from "@/components/Pagination";
import { getProductsListsApi, postAddProductApi } from "@/api/admin";
import { getToken } from "@/utils/auth";
export default {
name: "ProManagement",
components: { Pagination },
data() {
return {
listQuery: {
page: 1,
limit: 20,
},
total: 0,
listLoading: true,
list: [],
ossArr: [],
dialogVisible: false,
dialogTitle: "新增商品",
fieldFileList: [],
formData: {
os: "",
product_name: "",
type: "",
day: "",
night: "",
third_product_id: "",
trip_info: "",
},
fieldAction: process.env.VUE_APP_BASE_API + "/admin/upload/index",
fieldTypeOptions: [
{
label: "境内",
value: 1,
},
{
label: "境外",
value: 2,
},
],
rules: {
os: [
{
required: true,
message: "请选择平台",
trigger: "change",
},
],
product_name: [
{
required: true,
message: "请输入线路名称",
trigger: "blur",
},
],
day: [
{
required: true,
message: "请输入天数",
trigger: "blur",
},
],
night: [
{
required: true,
message: "请输入晚数",
trigger: "blur",
},
],
third_product_id: [
{
required: true,
message: "请输入第三方产品编号",
trigger: "blur",
},
],
type: [
{
required: true,
message: "请选择出游类型",
trigger: "change",
},
],
},
editType: "",
};
},
created() {
this.getList();
},
methods: {
handleCreate(type, item) {
this.editType = type;
type === "add" ? this.AddProduct() : this.editProduct(item);
},
AddProduct() {
this.dialogTitle = "新增商品";
this.dialogVisible = true;
},
editProduct(row) {
console.log(row, "row");
this.formData = { ...row };
this.dialogTitle = "编辑商品";
this.dialogVisible = true;
},
async getList() {
const res = await getProductsListsApi(this.listQuery);
console.log(res, "我我我");
if (res.error === 0) {
this.listLoading = false;
this.total = res.data.total;
this.list = res.data.data;
this.ossArr = res.ext.oss;
}
},
onWork(id) {
this.$router.push(`/system/proScheduling?id=${id}`);
},
handleChange(file, fileList) {
this.fileList = fileList.slice(-1);
},
async handlesAvatarSuccess(file) {
try {
var formdata = new FormData();
formdata.append("file", file.file);
const res = await this.$axios.post("/admin/upload/index", formdata, {
headers: {
"Content-type": "multipart/form-data",
"X-Token": getToken(),
},
});
console.log(res, "收拾收拾");
if (res.error === 0) {
file.onSuccess(res);
}
} catch (error) {
console.log(file, "error--handlesAvatarSuccess");
let uid = file.file.uid;
let idx = this.$refs.field105.uploadFiles.findIndex(
(item) => item.uid === uid
);
this.$refs.field105.uploadFiles.splice(idx, 1);
this.$message.error(`上传失败`);
}
},
handleWordSuccess(res, file, fileList, index) {
console.log(res, file, fileList, "成功了");
if (res) {
this.formData.trip_info = fileList
.map((item) => item.url || item.response.url)
.join(",");
this.fieldFileList = [
{
name: file.name,
uid: file.uid,
url: this.formData.trip_info,
},
];
this.$message.success("上传成功");
}
// if (!res) return;
// this.formData.trip_info = `${window.location.protocol}//${window.location.host}${res.data}`;
// this.fieldFileList = [
// {
// name: file.name,
// uid: file.uid,
// url: this.formData.trip_info,
// },
// ];
},
beforeRemove(file, fileList) {
return this.$confirm(`确定移除 ${file.name}`);
},
handleRemove(file, fileList) {
console.log(file, fileList, "handleRemove");
this.formData.trip_info = "";
this.fieldFileList.map((item, index) => {
if (item.uid === file.uid) {
this.fieldFileList.splice(index, 1);
}
});
},
handleUploadError(err, file) {
this.$message.error(`上传失败: ${file.name}`);
console.log(this.fieldFileList, "失败了");
this.fieldFileList.map((item, index) => {
if (item.uid === file.uid) {
this.fieldFileList.splice(index, 1);
}
});
},
wordBeforeUpload(file) {
const isRightType = [
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
].includes(file.type);
const isRightSize = file.size / 1024 / 1024 < 2;
if (!isRightType) {
this.$message.error("只允许上传 PDF、DOCX、XLSX 格式的文件");
}
if (!isRightSize) {
this.$message.error("文件大小超过 2MB");
}
return isRightType && isRightSize;
},
openLink(link) {
window.open(link);
},
onOpen() {},
onClose() {
this.$refs["elForm"].resetFields();
this.$refs.field105.uploadFiles = []; //
},
close() {
console.log("1111", this.$refs.field105.uploadFiles);
this.dialogVisible = false;
this.$refs.field105.uploadFiles = []; //
},
handelConfirm() {
this.$refs["elForm"].validate(async (valid) => {
if (!valid) return;
const res = await postAddProductApi(this.formData);
if (res.error === 0) {
this.getList();
this.$message({
message: this.editType === "add" ? "商品添加成功" : "商品编辑成功",
type: "success",
});
}
this.close();
});
},
},
};
</script>
<style scoped lang="scss">
.link-type {
color: rgb(7, 181, 249);
cursor: pointer;
}
</style>

View File

@ -0,0 +1,178 @@
<template>
<div class="pro_scheduling">
<el-calendar v-model="value">
<template #dateCell="{ date }">
<div>
<div>{{ date.getDate() }}</div>
<div v-if="isDateAvailable(date)">
<el-button
type="primary"
@click="bookDate(date)"
:disabled="!isDateAvailable(date)"
>
可预约
</el-button>
<div class="yy">已约 {{ bookedCount(date) }}</div>
<div class="yy">剩余 {{ availableCount(date) }}</div>
</div>
<div v-else>
<el-button disabled>不可预约</el-button>
<div class="test">已约 {{ bookedCount(date) }}</div>
<div class="test">剩余 0</div>
</div>
</div>
</template>
</el-calendar>
<el-dialog
:visible.sync="dialogVisible"
@open="onOpen"
@close="onClose"
title="预约行程"
>
<el-form
ref="elForm"
:model="formData"
:rules="rules"
size="medium"
label-width="100px"
>
<el-form-item label="出行日期" prop="date">
<el-input
v-model="formData.date"
placeholder="请输入单行文本单行文本"
:disabled="true"
clearable
:style="{ width: '100%' }"
></el-input>
</el-form-item>
<el-form-item label="可预约数量" prop="num">
<el-input
v-model="formData.num"
type="number"
placeholder="请输入可预约数量"
clearable
:style="{ width: '100%' }"
></el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="handelConfirm">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getProductSchedules, addProductSchedulesApi } from "@/api/admin";
export default {
data() {
return {
dialogVisible: false,
value: new Date(),
appointments: {},
formData: {
date: "",
num: "",
id: "",
},
rules: {
num: [
{
required: true,
message: "请输入可预约数量",
trigger: "blur",
},
],
},
};
},
created() {
this.formData.id = this.$route.query.id;
this.getList();
},
methods: {
async getList() {
const res = await getProductSchedules(this.formData.id);
if (res && res.error === 0) {
res.data.forEach((item) => {
this.$set(this.appointments, item.date, {
books_num: item.books_num,
left_num: item.left_num,
});
});
}
},
isDateAvailable(date) {
const today = new Date();
today.setHours(0, 0, 0, 0);
return date >= today;
},
bookDate(date) {
console.log(`预约日期: ${date.toLocaleDateString()}`);
this.dialogVisible = true;
this.formData.date = date.toLocaleDateString();
},
onOpen() {},
onClose() {
this.$refs["elForm"].resetFields();
},
close() {
this.dialogVisible = false;
},
handelConfirm() {
this.$refs["elForm"].validate(async (valid) => {
if (!valid) return;
const res = await addProductSchedulesApi(this.formData);
if (res.error === 0) {
this.$message.success("预约成功");
this.getList();
}
this.close();
});
},
},
computed: {
availableCount() {
return (date) => {
const dateString = date.toISOString().split("T")[0];
return this.appointments[dateString]?.left_num || 0;
};
},
bookedCount() {
return (date) => {
const dateString = date.toISOString().split("T")[0];
return this.appointments[dateString]?.books_num || 0;
};
},
},
};
</script>
<style scoped lang="scss">
.pro_scheduling {
display: flex;
flex-direction: column;
align-items: center;
.el-calendar {
margin: 20px 0;
}
}
.pro_scheduling ::v-deep .el-calendar-table .el-calendar-day {
height: 130px;
}
.test {
color: #c6ccd2;
font-size: 14px;
margin-top: 10px;
}
.yy {
color: #8a8c90;
font-size: 14px;
margin-top: 10px;
}
</style>