Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
SuperMan
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
罗超
SuperMan
Commits
75f7e212
Commit
75f7e212
authored
Oct 09, 2025
by
罗超
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新增报价
parent
9739c14a
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1972 additions
and
4 deletions
+1972
-4
BaseListManager.css
src/components/common/BaseListManager.css
+3
-0
QuoteDrawer.vue
src/pages/quoted-price/components/QuoteDrawer.vue
+1059
-0
create.vue
src/pages/quoted-price/create.vue
+167
-0
edit.vue
src/pages/quoted-price/edit.vue
+172
-0
price.vue
src/pages/quoted-price/price.vue
+443
-0
index.js
src/plug/index.js
+4
-4
config.js
src/router/config.js
+24
-0
quote.js
src/services/quote.js
+100
-0
No files found.
src/components/common/BaseListManager.css
View file @
75f7e212
...
...
@@ -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
src/pages/quoted-price/components/QuoteDrawer.vue
0 → 100644
View file @
75f7e212
This diff is collapsed.
Click to expand it.
src/pages/quoted-price/create.vue
0 → 100644
View file @
75f7e212
<
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
>
src/pages/quoted-price/edit.vue
0 → 100644
View file @
75f7e212
<
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
>
src/pages/quoted-price/price.vue
0 → 100644
View file @
75f7e212
This diff is collapsed.
Click to expand it.
src/plug/index.js
View file @
75f7e212
...
...
@@ -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
''
;
...
...
src/router/config.js
View file @
75f7e212
...
...
@@ -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
:
'编辑报价单'
}
},
]
}
src/services/quote.js
0 → 100644
View file @
75f7e212
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`
);
}
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment