Commit 75f7e212 authored by 罗超's avatar 罗超

新增报价

parent 9739c14a
......@@ -828,4 +828,7 @@
line-height: 14px;
min-width: 16px;
padding: 0 4px;
}
.el-drawer__header{
margin-bottom: 20px !important;
}
\ No newline at end of file
This diff is collapsed.
<template>
<div class="quote-create">
<div class="page-header">
<h2>创建报价单</h2>
<el-button @click="$router.go(-1)">返回</el-button>
</div>
<div class="form-container">
<el-form :model="form" ref="form" label-width="120px" class="quote-form">
<el-form-item label="选择行程" prop="itineraryId" required>
<el-select
v-model="form.itineraryId"
placeholder="请选择行程"
filterable
remote
:remote-method="searchItinerary"
:loading="itineraryLoading"
style="width: 100%"
>
<el-option
v-for="item in itineraryOptions"
:key="item.id"
:label="item.title"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="报价名称" prop="quoteName" required>
<el-input v-model="form.quoteName" placeholder="请输入报价名称" />
</el-form-item>
<el-form-item label="报价说明" prop="description">
<el-input
type="textarea"
v-model="form.description"
placeholder="请输入报价说明"
:rows="4"
/>
</el-form-item>
<el-form-item label="有效期" prop="validDate">
<el-date-picker
v-model="form.validDate"
type="date"
placeholder="选择有效期"
value-format="yyyy-MM-dd"
/>
</el-form-item>
</el-form>
<div class="form-actions">
<el-button @click="$router.go(-1)">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
创建报价单
</el-button>
</div>
</div>
</div>
</template>
<script>
import { quoteService } from '@/services/quote'
import { itineraryService } from '@/services/itinerary'
export default {
name: 'QuoteCreate',
data() {
return {
form: {
itineraryId: '',
quoteName: '',
description: '',
validDate: ''
},
itineraryOptions: [],
itineraryLoading: false,
submitting: false
}
},
methods: {
async searchItinerary(query) {
if (!query) return
this.itineraryLoading = true
try {
const response = await itineraryService.getItineraryList({
keyword: query,
pageIndex: 1,
pageSize: 20
})
if (response && response.data) {
this.itineraryOptions = response.data.list || []
}
} catch (error) {
console.error('搜索行程失败:', error)
} finally {
this.itineraryLoading = false
}
},
async handleSubmit() {
this.$refs.form.validate(async (valid) => {
if (!valid) return
this.submitting = true
try {
await quoteService.createQuote(this.form)
this.$message.success('创建成功')
this.$router.push('/quoted-price/price')
} catch (error) {
console.error('创建失败:', error)
this.$message.error('创建失败')
} finally {
this.submitting = false
}
})
}
}
}
</script>
<style scoped>
.quote-create {
padding: 20px;
background: #f5f5f5;
min-height: 100vh;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.page-header h2 {
margin: 0;
color: #333;
}
.form-container {
background: #fff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.quote-form {
max-width: 600px;
}
.form-actions {
margin-top: 30px;
text-align: center;
}
.form-actions .el-button {
margin: 0 10px;
min-width: 100px;
}
</style>
<template>
<div class="quote-edit">
<div class="page-header">
<h2>编辑报价单</h2>
<el-button @click="$router.go(-1)">返回</el-button>
</div>
<div class="form-container" v-loading="loading">
<el-form :model="form" ref="form" label-width="120px" class="quote-form">
<el-form-item label="行程名称">
<span>{{ form.itineraryName }}</span>
</el-form-item>
<el-form-item label="报价名称" prop="quoteName" required>
<el-input v-model="form.quoteName" placeholder="请输入报价名称" />
</el-form-item>
<el-form-item label="报价说明" prop="description">
<el-input
type="textarea"
v-model="form.description"
placeholder="请输入报价说明"
:rows="4"
/>
</el-form-item>
<el-form-item label="有效期" prop="validDate">
<el-date-picker
v-model="form.validDate"
type="date"
placeholder="选择有效期"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="总报价" prop="totalPrice">
<el-input-number
v-model="form.totalPrice"
:precision="2"
:step="100"
:min="0"
placeholder="请输入总报价"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态">
<el-option label="草稿" value="draft" />
<el-option label="待审核" value="pending" />
<el-option label="已通过" value="approved" />
<el-option label="已拒绝" value="rejected" />
</el-select>
</el-form-item>
</el-form>
<div class="form-actions">
<el-button @click="$router.go(-1)">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
保存修改
</el-button>
</div>
</div>
</div>
</template>
<script>
import { quoteService } from '@/services/quote'
export default {
name: 'QuoteEdit',
data() {
return {
form: {
id: '',
itineraryName: '',
quoteName: '',
description: '',
validDate: '',
totalPrice: 0,
status: 'draft'
},
loading: false,
submitting: false
}
},
created() {
this.loadQuoteDetail()
},
methods: {
async loadQuoteDetail() {
const id = this.$route.params.id
if (!id) return
this.loading = true
try {
const response = await quoteService.getQuoteDetail(id)
if (response && response.data) {
this.form = { ...response.data }
}
} catch (error) {
console.error('加载报价详情失败:', error)
this.$message.error('加载数据失败')
} finally {
this.loading = false
}
},
async handleSubmit() {
this.$refs.form.validate(async (valid) => {
if (!valid) return
this.submitting = true
try {
await quoteService.updateQuote(this.form)
this.$message.success('保存成功')
this.$router.push('/quoted-price/price')
} catch (error) {
console.error('保存失败:', error)
this.$message.error('保存失败')
} finally {
this.submitting = false
}
})
}
}
}
</script>
<style scoped>
.quote-edit {
padding: 20px;
background: #f5f5f5;
min-height: 100vh;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.page-header h2 {
margin: 0;
color: #333;
}
.form-container {
background: #fff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.quote-form {
max-width: 600px;
}
.form-actions {
margin-top: 30px;
text-align: center;
}
.form-actions .el-button {
margin: 0 10px;
min-width: 100px;
}
</style>
This diff is collapsed.
......@@ -123,7 +123,7 @@ export default {
let isOnline = 0; //0-本地测试,1-线上
let ocrUrl = "http://192.168.5.46:8888";
// domainUrl = "http://192.168.5.214";
domainUrl = "http://192.168.5.39:8083"
domainUrl = "http://192.168.5.214"
// domainUrl = "http://192.168.5.204:8030"
// domainUrl = "http://reborn.oytour.com";
let crmLocalFileStreamDownLoadUrl = "";
......@@ -1989,12 +1989,12 @@ export default {
}
Vue.prototype.deepFirstLetterToLower = function (obj) {
if (typeof obj !== 'object' || obj === null) return obj; // 非对象直接返回
// 处理数组:遍历每个元素递归转换
if (Array.isArray(obj)) {
return obj.map(item => deepFirstLetterToLower(item));
}
// 处理普通对象:遍历键值对递归转换
const newObj = {};
for (const key in obj) {
......@@ -2005,7 +2005,7 @@ export default {
}
return newObj;
}
// 基础首字母转小写函数(同上)
Vue.prototype.firstLetterToLower = function (str) {
if (typeof str !== 'string' || str.length === 0) return '';
......
......@@ -6825,5 +6825,29 @@ export default {
title: '编辑行程'
}
},
{
path: '/quoted-price/price',
name: 'quotedPrice',
component: resolve => require(['@/pages/quoted-price/price'], resolve),
meta: {
title: '报价管理'
}
},
{
path: '/quoted-price/create',
name: 'quotedPriceCreate',
component: resolve => require(['@/pages/quoted-price/create'], resolve),
meta: {
title: '创建报价单'
}
},
{
path: '/quoted-price/edit/:id',
name: 'quotedPriceEdit',
component: resolve => require(['@/pages/quoted-price/edit'], resolve),
meta: {
title: '编辑报价单'
}
},
]
}
import rwRequest from '@/plug/rwRequest';
export const quoteService = {
/**
* 获取报价列表
* @param {Object} params - 查询参数
* @returns {Promise} - API 响应
*/
getQuoteList: (params) => {
return rwRequest.get('/quote/list', { params });
},
/**
* 获取报价详情
* @param {string} id - 报价ID
* @returns {Promise} - API 响应
*/
getQuoteDetail: (id) => {
return rwRequest.get(`/quote/${id}`);
},
/**
* 创建报价
* @param {Object} data - 报价数据
* @returns {Promise} - API 响应
*/
createQuote: (data) => {
return rwRequest.post('/quote', data);
},
/**
* 更新报价
* @param {Object} data - 报价数据
* @returns {Promise} - API 响应
*/
updateQuote: (data) => {
return rwRequest.put('/quote', data);
},
/**
* 删除报价
* @param {string} id - 报价ID
* @returns {Promise} - API 响应
*/
deleteQuote: (id) => {
return rwRequest.delete(`/quote/${id}`);
},
/**
* 下载报价单
* @param {string} id - 报价ID
* @returns {Promise} - API 响应
*/
downloadQuote: (id) => {
return rwRequest.get(`/quote/${id}/download`, {
responseType: 'blob'
});
},
/**
* 根据行程ID生成报价
* @param {string} itineraryId - 行程ID
* @returns {Promise} - API 响应
*/
generateQuoteFromItinerary: (itineraryId) => {
return rwRequest.post(`/quote/generate/${itineraryId}`);
},
/**
* 审核报价
* @param {string} id - 报价ID
* @param {string} status - 审核状态 (approved/rejected)
* @param {string} remark - 审核备注
* @returns {Promise} - API 响应
*/
reviewQuote: (id, status, remark) => {
return rwRequest.post(`/quote/${id}/review`, {
status,
remark
});
},
/**
* 根据行程ID获取行程详情(用于报价单生成)
* @param {string} itineraryId - 行程ID
* @returns {Promise} - API 响应
*/
getItineraryForQuote: (itineraryId) => {
return rwRequest.get(`/itinerary/${itineraryId}/for-quote`);
},
/**
* 复制报价单
* @param {string} id - 报价ID
* @returns {Promise} - API 响应
*/
copyQuote: (id) => {
return rwRequest.post(`/quote/${id}/copy`);
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment