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() {
return request({
url: 'admin/products/list',
method: 'get'
})
return request({
url: "admin/products/list",
method: "get",
});
}
export function addProducts(data) {
return request({
url: '/admin/products/add',
method: 'post',
data
})
}
return request({
url: "/admin/products/add",
method: "post",
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
"
>
<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
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"

View File

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

View File

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