Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
J
jisu_gongdan_phone
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
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
zhanglongbao
jisu_gongdan_phone
Commits
5f2970ea
Commit
5f2970ea
authored
Aug 30, 2024
by
zhanglongbao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat
parent
ac1d469b
Changes
32
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
1491 additions
and
231 deletions
+1491
-231
components.d.ts
components.d.ts
+11
-0
index.html
index.html
+1
-2
axios.ts
src/api/axios.ts
+6
-3
index.ts
src/api/index.ts
+2
-0
external.ts
src/api/methods/external.ts
+12
-0
form.ts
src/api/methods/form.ts
+12
-3
oss.ts
src/api/methods/oss.ts
+8
-2
global.less
src/assets/css/global.less
+11
-2
index.ts
src/components/create_form_data/index.ts
+20
-0
index.vue
src/components/create_form_data/index.vue
+90
-0
index.vue
src/components/popup/index.vue
+0
-1
index.ts
src/components/select_address/index.ts
+17
-0
index.vue
src/components/select_address/index.vue
+50
-0
index.ts
src/components/select_client/index.ts
+17
-0
index.vue
src/components/select_client/index.vue
+123
-0
hook.ts
src/components/select_user_dept/hook.ts
+7
-5
index.ts
src/components/toast/index.ts
+20
-0
index.vue
src/components/toast/index.vue
+95
-0
index.vue
src/components/widget/field/address/index.vue
+59
-0
config.ts
src/components/widget/field/config.ts
+28
-2
index.vue
src/components/widget/field/image/index.vue
+0
-6
index.vue
src/components/widget/field/order_client/index.vue
+136
-0
index.vue
src/components/widget/field/order_product/index.vue
+126
-0
index.vue
src/components/widget/field/signature/index.vue
+126
-0
index.ts
src/router/index.ts
+12
-6
index.ts
src/utils/container/index.ts
+2
-0
directive.ts
src/utils/directive.ts
+1
-1
hook.ts
src/utils/hook.ts
+5
-2
public.ts
src/utils/public.ts
+7
-4
useTag.ts
src/utils/useTag.ts
+198
-192
index.vue
src/view/code_product_detail/index.vue
+282
-0
index.vue
src/view/home/index.vue
+7
-0
No files found.
components.d.ts
View file @
5f2970ea
...
...
@@ -7,8 +7,10 @@ export {}
/* prettier-ignore */
declare
module
'vue'
{
export
interface
GlobalComponents
{
Address
:
typeof
import
(
'./src/components/widget/field/address/index.vue'
)[
'default'
]
Avatar
:
typeof
import
(
'./src/components/avatar/index.vue'
)[
'default'
]
copy
:
typeof
import
(
'./src/components/widget/field/input copy/index.vue'
)[
'default'
]
Create_form_data
:
typeof
import
(
'./src/components/create_form_data/index.vue'
)[
'default'
]
Date_time
:
typeof
import
(
'./src/components/widget/field/date_time/index.vue'
)[
'default'
]
Date_time_range
:
typeof
import
(
'./src/components/widget/field/date_time_range/index.vue'
)[
'default'
]
Dd
:
typeof
import
(
'./src/components/name/dd.vue'
)[
'default'
]
...
...
@@ -19,22 +21,31 @@ declare module 'vue' {
Multiline
:
typeof
import
(
'./src/components/widget/field/multiline/index.vue'
)[
'default'
]
Name
:
typeof
import
(
'./src/components/name/index.vue'
)[
'default'
]
Number
:
typeof
import
(
'./src/components/widget/field/number/index.vue'
)[
'default'
]
Order_client
:
typeof
import
(
'./src/components/widget/field/order_client/index.vue'
)[
'default'
]
Order_product
:
typeof
import
(
'./src/components/widget/field/order_product/index.vue'
)[
'default'
]
Parting_line
:
typeof
import
(
'./src/components/widget/field/parting_line/index.vue'
)[
'default'
]
Popup
:
typeof
import
(
'./src/components/popup/index.vue'
)[
'default'
]
RouterLink
:
typeof
import
(
'vue-router'
)[
'RouterLink'
]
RouterView
:
typeof
import
(
'vue-router'
)[
'RouterView'
]
Select
:
typeof
import
(
'./src/components/select/index.vue'
)[
'default'
]
Select_address
:
typeof
import
(
'./src/components/select_address/index.vue'
)[
'default'
]
Select_client
:
typeof
import
(
'./src/components/select_client/index.vue'
)[
'default'
]
Select_customer
:
typeof
import
(
'./src/components/widget/field/select_customer/index.vue'
)[
'default'
]
Select_time
:
typeof
import
(
'./src/components/select_time/index.vue'
)[
'default'
]
Select_user_dept
:
typeof
import
(
'./src/components/select_user_dept/index.vue'
)[
'default'
]
Signature
:
typeof
import
(
'./src/components/widget/field/signature/index.vue'
)[
'default'
]
Single_choice
:
typeof
import
(
'./src/components/widget/field/single_choice/index.vue'
)[
'default'
]
Toast
:
typeof
import
(
'./src/components/toast/index.vue'
)[
'default'
]
User
:
typeof
import
(
'./src/components/widget/field/user/index.vue'
)[
'default'
]
User_dept_list
:
typeof
import
(
'./src/components/user_dept_list/index.vue'
)[
'default'
]
User_list
:
typeof
import
(
'./src/components/user_list/index.vue'
)[
'default'
]
VanButton
:
typeof
import
(
'vant/es'
)[
'Button'
]
VanCascader
:
typeof
import
(
'vant/es'
)[
'Cascader'
]
VanCheckbox
:
typeof
import
(
'vant/es'
)[
'Checkbox'
]
VanDatePicker
:
typeof
import
(
'vant/es'
)[
'DatePicker'
]
VanPicker
:
typeof
import
(
'vant/es'
)[
'Picker'
]
VanPopup
:
typeof
import
(
'vant/es'
)[
'Popup'
]
VanRadio
:
typeof
import
(
'vant/es'
)[
'Radio'
]
VanStepper
:
typeof
import
(
'vant/es'
)[
'Stepper'
]
VanTimePicker
:
typeof
import
(
'vant/es'
)[
'TimePicker'
]
Widget
:
typeof
import
(
'./src/components/widget/index.vue'
)[
'default'
]
...
...
index.html
View file @
5f2970ea
...
...
@@ -2,9 +2,8 @@
<html
lang=
"en"
>
<head>
<meta
charset=
"UTF-8"
/>
<link
rel=
"icon"
type=
"image/svg+xml"
href=
"/vite.svg"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<title>
Vite + Vue + TS
</title>
<title>
极速工单
</title>
</head>
<body>
<div
id=
"app"
></div>
...
...
src/api/axios.ts
View file @
5f2970ea
import
axios
from
"axios"
;
import
{
getToken
,
getUrlKey
}
from
"@/utils/public"
;
import
{
login
}
from
"@/utils/container/index"
;
import
{
showToast
}
from
"vant"
;
import
{
$toast
}
from
"@/components/toast"
;
const
external_token
=
getUrlKey
(
"external_token"
)
||
"cr2pc8jsibf027753ppg"
;
const
http
=
axios
.
create
({
baseURL
:
"/api"
,
...
...
@@ -14,6 +16,7 @@ http.interceptors.request.use((config) => {
arg
:
{
...(
config
.
data
||
{}),
token
,
external_token
,
},
meta
:
{
app_id
:
getUrlKey
(
"corp_id"
)
||
undefined
,
...
...
@@ -25,12 +28,12 @@ http.interceptors.request.use((config) => {
http
.
interceptors
.
response
.
use
((
response
)
=>
{
if
(
response
.
status
==
200
)
{
if
(
response
.
data
.
code
==
1000
)
{
showToast
({
message
:
"登陆过期"
,
position
:
"top
"
});
$toast
({
type
:
"warning"
,
message
:
"登陆过期
"
});
login
();
}
if
(
!
[
0
,
1000
].
includes
(
response
.
data
.
code
))
{
showToast
({
message
:
response
.
data
.
msg
,
position
:
"top"
});
$toast
({
type
:
"warning"
,
message
:
response
.
data
.
msg
});
}
return
response
.
data
;
...
...
src/api/index.ts
View file @
5f2970ea
...
...
@@ -8,6 +8,7 @@ import oss from "./methods/oss";
import
order
from
"./methods/order"
;
import
log
from
"./methods/log"
;
import
power
from
"./methods/power"
;
import
external
from
"./methods/external"
;
export
default
{
platform
,
...
...
@@ -20,4 +21,5 @@ export default {
order
,
log
,
power
,
external
,
};
src/api/methods/external.ts
0 → 100644
View file @
5f2970ea
import
http
from
"../axios"
;
export
default
{
// 获取产品信息
getInfoByQrCode
()
{
return
http
.
post
(
"/order.external/getInfoByQrCode"
);
},
// 获取详情界面配置
getQrCodeWindow
()
{
return
http
.
post
(
"/order.external/getQrCodeWindow"
);
},
};
src/api/methods/form.ts
View file @
5f2970ea
import
http
from
"../axios"
;
import
{
container
}
from
"@/utils/container/index"
;
export
default
{
// 获取表单类型列表
TemplateLists
(
params
:
{
template_type
?:
number
[];
form_ids
?:
string
[]
})
{
return
http
.
post
(
"/order.form/TemplateLists"
,
params
);
return
http
.
post
(
`/order.
${
container
.
qr_code
?
"external"
:
"form"
}
/TemplateLists`
,
params
);
},
// 获取工单类类型详情
FormDetail
(
params
:
{
id
:
string
})
{
return
http
.
post
(
"/order.form/FromDetail"
,
params
);
return
http
.
post
(
`/order.
${
container
.
qr_code
?
"external"
:
"form"
}
/FromDetail`
,
params
);
},
// 更新表单
FormUpdate
(
params
:
{
form_ids
:
string
[]
})
{
...
...
@@ -31,7 +38,9 @@ export default {
},
// 获取省市区地址
GetAddress
()
{
return
http
.
post
(
"/order.form/GetAddress"
);
return
http
.
post
(
`/order.
${
container
.
qr_code
?
"external"
:
"form"
}
/GetAddress`
);
},
// 获取表单工作流
listFlowNode
(
params
:
{
form_id
:
string
})
{
...
...
src/api/methods/oss.ts
View file @
5f2970ea
import
http
from
"../axios"
;
import
{
container
}
from
"@/utils/container/index"
;
export
default
{
// 获取oss配置
getOssConfig
()
{
return
http
.
post
(
"/order.file/getOssConfig"
);
return
http
.
post
(
`/order.
${
container
.
qr_code
?
"external"
:
"file"
}
/getOssConfig`
);
},
// 文件签名
getSignByMap
(
params
:
any
)
{
return
http
.
post
(
"/order.file/getSignByMap"
,
params
);
return
http
.
post
(
`/order.
${
container
.
qr_code
?
"external"
:
"file"
}
/getSignByMap`
,
params
);
},
};
src/assets/css/global.less
View file @
5f2970ea
...
...
@@ -106,7 +106,7 @@ body {
.button {
--btn_bg_color: var(--blue);
--btn_color: white;
--btn_height: 2
9
px;
--btn_height: 2
8
px;
color: var(--btn_color);
background-color: var(--btn_bg_color);
...
...
@@ -114,12 +114,12 @@ body {
border-radius: 4px;
cursor: pointer;
user-select: none;
font-size: 12px;
box-sizing: border-box;
height: var(--btn_height);
line-height: var(--btn_height);
display: inline-block;
text-align: center;
flex-shrink: 0;
&:hover {
opacity: 0.8;
...
...
@@ -162,6 +162,15 @@ body {
background-color: transparent;
}
}
.bottom_button {
display: flex;
align-items: center;
.button {
--btn_height: 38px;
flex: 1;
}
}
// 标签
.tags {
...
...
src/components/create_form_data/index.ts
0 → 100644
View file @
5f2970ea
import
{
createApp
}
from
"vue"
;
import
comp
from
"./index.vue"
;
import
type
{
Props
}
from
"./index.vue"
;
import
{
initApp
}
from
"@/utils/init"
;
// 修改表单信息
export
const
$createFormData
=
(
options
:
Props
)
=>
{
const
container
=
document
.
createElement
(
"div"
);
options
.
onHide
=
()
=>
{
vm
.
unmount
();
document
.
body
.
removeChild
(
container
);
container
.
remove
();
};
const
vm
=
createApp
(
comp
,
options
as
any
);
initApp
(
vm
);
vm
.
mount
(
container
);
document
.
body
.
appendChild
(
container
);
};
src/components/create_form_data/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<popupComp
ref=
"popup_comp"
:show_header=
"false"
:round=
"false"
height=
"100vh"
@
closed=
"onHide"
>
<div
class=
"formContent"
>
<formContentComp
:form_detail=
"form_detail"
v-model=
"data"
@
editData=
"editData"
/>
<div
class=
"bottom bottom_button"
>
<span
class=
"button plain"
@
click=
"close"
>
取消
</span>
<span
class=
"button"
@
click=
"confirm"
>
创建
</span>
</div>
</div>
</popupComp>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
}
from
"vue"
;
import
popupComp
from
"@/components/popup/index.vue"
;
import
api
from
"@/api"
;
import
formContentComp
from
"@/components/widget/index.vue"
;
export
interface
Props
{
form_id
:
string
;
data
?:
object
;
onHide
?:
()
=>
void
;
onConfirm
?:
(
data
:
object
)
=>
void
;
onEditConfirm
?:
(
data
:
object
)
=>
void
;
}
const
props
=
defineProps
<
Props
>
();
const
show
=
ref
(
false
);
onMounted
(()
=>
{
show
.
value
=
true
;
});
const
confirm
=
()
=>
{
props
.
onConfirm
?.(
data
.
value
);
props
.
onEditConfirm
?.(
edit
.
value
);
close
();
};
const
popup_comp
=
ref
();
const
close
=
()
=>
{
popup_comp
.
value
.
show
=
false
;
};
// 表单数据
const
data
=
ref
<
object
>
({});
const
edit
=
ref
<
object
>
({});
const
editData
=
(
data
:
object
)
=>
{
edit
.
value
=
{
...
edit
.
value
,
...
data
};
console
.
log
(
"edit"
,
edit
.
value
);
};
// 表单信息
const
form_detail
=
ref
<
FormDetail
>
({}
as
FormDetail
);
const
getFormDetail
=
async
()
=>
{
const
msg
=
await
api
.
form
.
FormDetail
({
id
:
props
.
form_id
,
});
if
(
msg
.
code
==
0
)
{
form_detail
.
value
=
msg
.
data
[
0
]
||
{};
}
};
// 初始化
const
init
=
()
=>
{
getFormDetail
();
if
(
props
.
data
)
{
data
.
value
=
JSON
.
parse
(
JSON
.
stringify
(
props
.
data
));
}
};
init
();
</
script
>
<
style
lang=
"less"
scoped
>
.formContent {
height: 100vh;
display: flex;
flex-direction: column;
.bottom {
padding: 8px 20px;
flex-shrink: 0;
}
}
</
style
>
src/components/popup/index.vue
View file @
5f2970ea
...
...
@@ -34,7 +34,6 @@ interface PopupProps {
}
withDefaults
(
defineProps
<
PopupProps
>
(),
{
position
:
"bottom"
,
height
:
"30%"
,
round
:
true
,
show_header
:
true
,
show_cancel
:
true
,
...
...
src/components/select_address/index.ts
0 → 100644
View file @
5f2970ea
import
{
createApp
}
from
"vue"
;
import
Comp
from
"./index.vue"
;
import
type
{
Props
}
from
"./index.vue"
;
export
const
$selectAddress
=
(
options
:
Props
)
=>
{
const
container
=
document
.
createElement
(
"div"
);
options
.
onHide
=
()
=>
{
vm
.
unmount
();
document
.
body
.
removeChild
(
container
);
container
.
remove
();
};
const
vm
=
createApp
(
Comp
,
options
as
any
);
vm
.
mount
(
container
);
document
.
body
.
appendChild
(
container
);
};
src/components/select_address/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<popupComp
ref=
"popup_comp"
title=
"选择地址"
@
closed=
"onHide"
@
cancel=
"close"
@
confirm=
"confirm"
>
<van-cascader
:options=
"addressList"
:show-header=
"false"
:field-names=
"
{
text: 'name',
value: 'name',
children: 'districts',
}"
@change="change" />
</popupComp>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
"vue"
;
import
{
useAddress
}
from
"@/utils/hook"
;
import
popupComp
from
"@/components/popup/index.vue"
;
export
interface
Props
{
value
?:
string
;
onHide
?():
void
;
onConfirm
?(
value
:
string
):
void
;
}
const
props
=
withDefaults
(
defineProps
<
Props
>
(),
{});
const
popup_comp
=
ref
();
const
select_address
=
ref
<
string
>
(
""
);
const
{
addressList
}
=
useAddress
();
const
change
=
(
value
:
any
)
=>
{
select_address
.
value
=
value
.
selectedOptions
.
map
((
it
:
any
)
=>
it
.
name
)
.
join
(
"/"
);
};
const
close
=
()
=>
{
popup_comp
.
value
.
show
=
false
;
};
const
confirm
=
()
=>
{
props
.
onConfirm
?.(
select_address
.
value
);
close
();
};
</
script
>
<
style
lang=
"less"
scoped
></
style
>
src/components/select_client/index.ts
0 → 100644
View file @
5f2970ea
import
{
createApp
}
from
"vue"
;
import
Comp
from
"./index.vue"
;
import
type
{
Props
}
from
"./index.vue"
;
export
const
$selectClient
=
(
options
:
Props
)
=>
{
const
container
=
document
.
createElement
(
"div"
);
options
.
onHide
=
()
=>
{
vm
.
unmount
();
document
.
body
.
removeChild
(
container
);
container
.
remove
();
};
const
vm
=
createApp
(
Comp
,
options
as
any
);
vm
.
mount
(
container
);
document
.
body
.
appendChild
(
container
);
};
src/components/select_client/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<popupComp
ref=
"popup_comp"
title=
"选择客户"
@
closed=
"onHide"
@
cancel=
"close"
@
confirm=
"confirm"
>
<div
class=
"list"
>
<div
v-for=
"it in list"
class=
"customer"
:class=
"
{ active: selected_client.id == it.id }"
@click="selected_client = it">
<div
class=
"name"
>
<span>
{{
it
.
customer_name
}}
</span>
<i
class=
"icon-24"
v-if=
"selected_client.id == it.id"
></i>
<i
class=
"icon-14"
v-else
></i>
</div>
<div
class=
"info"
>
<span>
联系人:
{{
it
.
link_user_name
}}
</span>
<span>
联系电话:
{{
it
.
link_user_phone
}}
</span>
</div>
</div>
</div>
</popupComp>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
"vue"
;
import
api
from
"@/api"
;
import
{
useDataList
}
from
"@/utils/hook"
;
import
popupComp
from
"@/components/popup/index.vue"
;
export
interface
Props
{
value
?:
any
;
onHide
?():
void
;
onConfirm
?(
value
:
any
):
void
;
}
const
props
=
withDefaults
(
defineProps
<
Props
>
(),
{});
const
popup_comp
=
ref
();
const
close
=
()
=>
{
popup_comp
.
value
.
show
=
false
;
};
const
confirm
=
()
=>
{
props
.
onConfirm
?.(
selected_client
.
value
);
close
();
};
// 选中客户
const
selected_client
=
ref
<
any
>
(
props
.
value
||
{});
// 产品表单
const
client_form_list
=
ref
<
FormInfo
[]
>
([]);
const
getClientFormList
=
async
()
=>
{
const
msg
=
await
api
.
form
.
TemplateLists
({
template_type
:
[
1
]
});
if
(
msg
.
code
==
0
)
{
client_form_list
.
value
=
msg
.
data
.
lists
;
}
};
// 数据列表
const
params
=
computed
(()
=>
({
api
:
api
.
customer
.
listCustomInfo
,
params
:
{
form_id
:
client_form_list
.
value
[
0
].
form_id
,
size
:
1000
,
},
}));
const
{
list
,
getList
}
=
useDataList
(
params
);
const
init
=
async
()
=>
{
await
getClientFormList
();
getList
();
};
init
();
</
script
>
<
style
lang=
"less"
scoped
>
.list {
display: flex;
flex-direction: column;
padding: 10px;
height: 400px;
overflow: auto;
.customer {
display: flex;
flex-direction: column;
border: 1px solid var(--border_color);
border-radius: 2px;
cursor: pointer;
> div {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 14px;
}
.name {
background-color: var(--bg_gray);
height: 34px;
}
.info {
height: 38px;
}
&.active {
border-color: var(--blue);
color: var(--blue);
.name {
background-color: var(--hover_blue);
}
}
}
.customer:not(:nth-last-of-type(1)) {
margin-bottom: 10px;
}
}
</
style
>
src/components/select_user_dept/hook.ts
View file @
5f2970ea
import
{
computed
,
ref
}
from
"vue"
;
import
api
from
"@/api"
;
import
{
showToast
}
from
"van
t"
;
import
{
$toast
}
from
"@/components/toas
t"
;
import
{
Team
}
from
"@/utils/hook"
;
/**
...
...
@@ -203,10 +203,11 @@ export const useUserList = (props: Props) => {
depts
.
value
[
dept_id
].
length
>
select_item
.
value
.
size
!
)
{
showToast
({
$toast
({
type
:
"warning"
,
message
:
"全选超出数量限制,请手动选择"
,
position
:
"top"
,
});
return
;
}
...
...
@@ -223,10 +224,11 @@ export const useUserList = (props: Props) => {
users
.
value
[
dept_id
].
length
>
select_item
.
value
.
size
!
)
{
showToast
({
$toast
({
type
:
"warning"
,
message
:
"全选超出数量限制,请手动选择"
,
position
:
"top"
,
});
return
;
}
users
.
value
[
dept_id
].
forEach
((
it
)
=>
{
...
...
src/components/toast/index.ts
0 → 100644
View file @
5f2970ea
import
{
createApp
}
from
"vue"
;
import
comp
from
"./index.vue"
;
import
type
{
Props
}
from
"./index.vue"
;
import
{
initApp
}
from
"@/utils/init"
;
// toast
export
const
$toast
=
(
options
:
Props
)
=>
{
const
container
=
document
.
createElement
(
"div"
);
options
.
onHide
=
()
=>
{
vm
.
unmount
();
document
.
body
.
removeChild
(
container
);
container
.
remove
();
};
const
vm
=
createApp
(
comp
,
options
as
any
);
initApp
(
vm
);
vm
.
mount
(
container
);
document
.
body
.
appendChild
(
container
);
};
src/components/toast/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<Transition>
<div
v-if=
"show"
class=
"toast"
:style=
"
{
color: config.color,
backgroundColor: config.bg_color,
border: `1px solid ${config.border}`,
}">
<i
:class=
"config.icon"
></i>
<span>
{{
message
}}
</span>
</div>
</Transition>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
,
onMounted
}
from
"vue"
;
export
interface
Props
{
type
?:
"warning"
|
"success"
|
"error"
;
message
:
string
;
onHide
?:
()
=>
void
;
}
const
props
=
defineProps
<
Props
>
();
const
show
=
ref
(
false
);
onMounted
(()
=>
{
show
.
value
=
true
;
});
setTimeout
(()
=>
{
show
.
value
=
false
;
setTimeout
(()
=>
{
props
.
onHide
?.();
},
1000
);
},
1000
);
const
config
=
computed
(()
=>
{
if
(
props
.
type
==
"warning"
)
{
return
{
icon
:
"icon-131"
,
color
:
"#E6A23D"
,
bg_color
:
"#FDF6EC"
,
border
:
"#FAECD8"
,
};
}
else
if
(
props
.
type
==
"success"
)
{
return
{
icon
:
"icon-134"
,
color
:
"#67C23A"
,
bg_color
:
"#F0F9EB"
,
border
:
"#E1F3D8"
,
};
}
else
if
(
props
.
type
==
"error"
)
{
return
{
icon
:
"icon-69"
,
color
:
"#F56C6C"
,
bg_color
:
"#FEF0F0"
,
border
:
"#FDE2E2"
,
};
}
return
{
icon
:
"icon-131"
,
color
:
"#909399"
,
bg_color
:
"#f4f4f5"
,
border
:
"#ececee"
,
};
});
</
script
>
<
style
lang=
"less"
scoped
>
.toast {
position: fixed;
z-index: 20000;
top: 10px;
left: 50%;
transform: translateX(-50%);
padding: 8px 10px;
border-radius: 4px;
font-size: 14px;
span {
margin-left: 4px;
}
}
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</
style
>
src/components/widget/field/address/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<div
class=
"box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
{{
config
.
key_name
}}
</span>
</div>
<div
class=
"content"
>
<span
class=
"border"
@
click=
"selectAddress"
>
{{
address
.
address
}}
</span>
</div>
</div>
<div
class=
"box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
详细地址
</span>
</div>
<div
class=
"content"
>
<input
type=
"text"
class=
"border"
v-model=
"address.detailed_address"
@
input=
"change"
/>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
"vue"
;
import
{
FieldProps
}
from
"../config"
;
import
{
$selectAddress
}
from
"@/components/select_address/index"
;
const
props
=
defineProps
<
FieldProps
>
();
const
emit
=
defineEmits
([
"edit"
]);
const
address
=
ref
<
{
address
:
string
;
detailed_address
:
string
;
type
:
"1"
|
"2"
;
}
>
((
props
.
data
[
props
.
config
.
key_id
]
||
{})
as
any
);
const
change
=
()
=>
{
emit
(
"edit"
,
{
[
props
.
config
.
key_id
]:
address
.
value
});
};
// 选择地址
const
selectAddress
=
()
=>
{
$selectAddress
({
onConfirm
:
(
data
:
string
)
=>
{
address
.
value
.
address
=
data
;
change
();
},
});
};
</
script
>
<
style
lang=
"less"
scoped
>
@import "../global.less";
</
style
>
src/components/widget/field/config.ts
View file @
5f2970ea
...
...
@@ -108,7 +108,33 @@ export const baseFieldConfig: FieldConfig[] = [
},
];
// 进阶字段
export
const
upgradeFieldConfig
:
FieldConfig
[]
=
[];
export
const
upgradeFieldConfig
:
FieldConfig
[]
=
[
{
title
:
"地址选择"
,
icon
:
"icon-301"
,
key_type
:
21
,
component
:
markRaw
(
defineAsyncComponent
(()
=>
import
(
"./address/index.vue"
))
),
},
{
title
:
"手写签名"
,
icon
:
"icon-301"
,
key_type
:
31
,
component
:
markRaw
(
defineAsyncComponent
(()
=>
import
(
"./signature/index.vue"
))
),
},
];
// 自定义widget_key_name
export
const
widgetKeyName
:
{
[
key
:
string
]:
any
}
=
{};
export
const
widgetKeyName
:
{
[
key
:
string
]:
any
}
=
{
// 工单客户(展示联系人联系电话
order_client
:
markRaw
(
defineAsyncComponent
(()
=>
import
(
"./order_client/index.vue"
))
),
// 工单产品(展示编号型号
order_product
:
markRaw
(
defineAsyncComponent
(()
=>
import
(
"./order_product/index.vue"
))
),
};
export
const
allFieldConfig
=
[...
baseFieldConfig
,
...
upgradeFieldConfig
];
src/components/widget/field/image/index.vue
View file @
5f2970ea
...
...
@@ -3,12 +3,6 @@
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
{{
config
.
key_name
}}
</span>
<el-tooltip
:content=
"config.key_text"
placement=
"top"
v-if=
"config.key_text"
>
<i
class=
"icon-22"
></i>
</el-tooltip>
</div>
<div
class=
"content"
>
...
...
src/components/widget/field/order_client/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<div
class=
"box client_box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
{{
config
.
key_name
}}
</span>
<div
class=
"right"
>
<span>
新增客户
</span>
<span>
<i
class=
"icon-141"
></i>
刷新
</span>
</div>
</div>
<div
class=
"content"
>
<div
class=
"border"
@
click=
"selectClient"
>
<span>
{{
client
.
customer_name
}}
</span>
<i
class=
"icon-321"
></i>
</div>
</div>
</div>
<div
class=
"box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
联系人
</span>
</div>
<div
class=
"content"
>
<span
class=
"border"
>
{{
client
.
link_user_name
}}
</span>
</div>
</div>
<div
class=
"box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
联系电话
</span>
</div>
<div
class=
"content"
>
<span
class=
"border"
>
{{
client
.
link_user_phone
}}
</span>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
"vue"
;
import
{
FieldProps
}
from
"../config"
;
import
{
$selectClient
}
from
"@/components/select_client/index"
;
const
props
=
defineProps
<
FieldProps
>
();
const
emit
=
defineEmits
([
"edit"
]);
const
change
=
()
=>
{
emit
(
"edit"
,
{
[
props
.
config
.
key_id
]:
client
.
value
.
id
,
customer_name
:
client
.
value
.
link_user_name
,
customer_phone
:
client
.
value
.
link_user_phone
,
});
};
const
client
=
ref
<
any
>
({});
const
init
=
()
=>
{
client
.
value
=
props
.
data
[
props
.
config
.
key_id
]
||
{};
};
init
();
const
selectClient
=
()
=>
{
$selectClient
({
value
:
client
.
value
,
onConfirm
:
(
data
)
=>
{
client
.
value
=
data
;
console
.
log
(
"client"
,
client
.
value
);
change
();
},
});
};
</
script
>
<
style
lang=
"less"
scoped
>
@import "../global.less";
.client_box {
.label {
width: 100% !important;
.title {
flex: 1;
}
.right {
display: flex;
align-items: center;
color: var(--blue);
font-size: 13px;
span {
display: flex;
align-items: center;
padding-left: 14px;
i {
margin-right: 4px;
}
}
span:nth-of-type(1) {
border-right: 1px solid var(--blue);
padding-right: 14px;
}
}
}
.content {
.border {
display: flex;
justify-content: space-between;
position: relative;
i {
position: absolute;
right: 0;
top: 0;
height: 100%;
box-sizing: border-box;
width: 40px;
border-left: 1px solid var(--border_color);
display: flex;
align-items: center;
justify-content: center;
color: var(--orange);
font-size: 16px;
}
}
}
}
</
style
>
src/components/widget/field/order_product/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<div
class=
"box product_box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
{{
config
.
key_name
}}
</span>
<div
class=
"right"
>
<span>
新增客户
</span>
<span>
<i
class=
"icon-141"
></i>
刷新
</span>
</div>
</div>
<div
class=
"content"
>
<div
class=
"border"
@
click=
"selectClient"
>
<span>
{{
client
.
customer_name
}}
</span>
<i
class=
"icon-321"
></i>
</div>
</div>
</div>
<div
class=
"box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
联系人
</span>
</div>
<div
class=
"content"
>
<span
class=
"border"
>
{{
client
.
link_user_name
}}
</span>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
"vue"
;
import
{
FieldProps
}
from
"../config"
;
import
{
$selectClient
}
from
"@/components/select_client/index"
;
const
props
=
defineProps
<
FieldProps
>
();
const
emit
=
defineEmits
([
"edit"
]);
const
change
=
()
=>
{
emit
(
"edit"
,
{
[
props
.
config
.
key_id
]:
client
.
value
.
id
,
customer_name
:
client
.
value
.
link_user_name
,
customer_phone
:
client
.
value
.
link_user_phone
,
});
};
const
client
=
ref
<
any
>
({});
const
init
=
()
=>
{
client
.
value
=
props
.
data
[
props
.
config
.
key_id
]
||
{};
};
init
();
const
selectClient
=
()
=>
{
$selectClient
({
value
:
client
.
value
,
onConfirm
:
(
data
)
=>
{
client
.
value
=
data
;
console
.
log
(
"client"
,
client
.
value
);
change
();
},
});
};
</
script
>
<
style
lang=
"less"
scoped
>
@import "../global.less";
.product_box {
.label {
width: 100% !important;
.title {
flex: 1;
}
.right {
display: flex;
align-items: center;
color: var(--blue);
font-size: 13px;
span {
display: flex;
align-items: center;
padding-left: 14px;
i {
margin-right: 4px;
}
}
span:nth-of-type(1) {
border-right: 1px solid var(--blue);
padding-right: 14px;
}
}
}
.content {
.border {
display: flex;
justify-content: space-between;
position: relative;
i {
position: absolute;
right: 0;
top: 0;
height: 100%;
box-sizing: border-box;
width: 40px;
border-left: 1px solid var(--border_color);
display: flex;
align-items: center;
justify-content: center;
color: var(--orange);
font-size: 16px;
}
}
}
}
</
style
>
src/components/widget/field/signature/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<div
class=
"box"
>
<div
class=
"label"
>
<span
class=
"required"
v-if=
"required"
>
*
</span>
<span
class=
"title"
>
{{
config
.
key_name
}}
</span>
</div>
<div
class=
"content"
>
<canvas
ref=
"canvas_comp"
></canvas>
<div
class=
"btn_group"
>
<span
class=
"button plain"
@
click=
"signature.clear"
>
清空
</span>
<span
class=
"button"
@
click=
"save"
>
确定
</span>
</div>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
onMounted
,
ref
}
from
"vue"
;
import
{
FieldProps
}
from
"../config"
;
import
SmoothSignature
from
"smooth-signature"
;
import
{
useOssStore
}
from
"@/store/oss"
;
import
axios
from
"axios"
;
import
{
ossSignature
}
from
"@/utils/ossSignature"
;
import
{
$toast
}
from
"@/components/toast/index"
;
const
props
=
defineProps
<
FieldProps
>
();
const
emit
=
defineEmits
([
"edit"
]);
const
Oss
=
useOssStore
();
const
change
=
(
id
:
string
)
=>
{
emit
(
"edit"
,
{
[
props
.
config
.
key_id
]:
id
});
};
const
canvas_comp
=
ref
();
const
signature
=
ref
();
onMounted
(()
=>
{
signature
.
value
=
new
SmoothSignature
(
canvas_comp
.
value
,
{
height
:
200
,
minWidth
:
3
,
maxWidth
:
8
,
});
init
();
});
const
save
=
async
()
=>
{
if
(
signature
.
value
.
isEmpty
())
{
$toast
({
type
:
"warning"
,
message
:
"无签名内容"
});
return
;
}
const
base64
=
signature
.
value
.
getPNG
();
const
buffer
=
base64ToBinary
(
base64
);
// 上传文件
const
data
:
any
=
Oss
.
getBaseData
({
name
:
new
Date
().
getTime
()
+
".png"
,
}
as
any
);
const
params
=
new
FormData
();
Object
.
keys
(
data
).
forEach
((
key
)
=>
{
params
.
set
(
key
,
data
[
key
]);
});
params
.
append
(
"file"
,
buffer
as
any
);
const
msg
=
await
axios
.
post
(
Oss
.
base_oss
.
host
,
params
);
if
(
msg
.
data
.
code
==
0
)
{
change
(
msg
.
data
.
data
.
data
.
id
);
}
};
// 将base64图片转换成二进制
function
base64ToBinary
(
base64Data
:
string
)
{
let
binaryString
=
window
.
atob
(
base64Data
.
split
(
","
)[
1
]);
let
arrayBuffer
=
new
ArrayBuffer
(
binaryString
.
length
);
let
intArray
=
new
Uint8Array
(
arrayBuffer
);
for
(
let
i
=
0
,
j
=
binaryString
.
length
;
i
<
j
;
i
++
)
{
intArray
[
i
]
=
binaryString
.
charCodeAt
(
i
);
}
let
data
=
[
intArray
];
const
blob
=
new
Blob
(
data
);
return
blob
;
}
const
init
=
async
()
=>
{
const
file_url
=
props
.
data
[
props
.
config
.
key_id
]?.
file_url
||
""
;
if
(
!
file_url
)
return
;
const
ctx
=
canvas_comp
.
value
.
getContext
(
"2d"
);
// 创建一个新的 Image 对象
const
img
=
new
Image
();
// 设置图片的 src 属性
const
url
=
await
ossSignature
(
file_url
);
img
.
src
=
url
;
// 等待图片加载完成
img
.
onload
=
function
()
{
// 将图片绘制到 Canvas 上
ctx
.
drawImage
(
img
,
0
,
0
,
canvas_comp
.
value
.
width
,
canvas_comp
.
value
.
height
);
};
};
</
script
>
<
style
lang=
"less"
scoped
>
@import "../global.less";
.content {
display: flex;
flex-direction: column;
canvas {
border: 1px solid var(--border_color);
border-radius: 4px;
margin: 10px 0;
}
.btn_group {
display: flex;
justify-content: flex-end;
}
}
</
style
>
src/router/index.ts
View file @
5f2970ea
...
...
@@ -8,7 +8,12 @@ const routers: RouteRecordRaw[] = [
{
path
:
"/"
,
name
:
"home"
,
component
:
()
=>
import
(
"@/view/ceshi.vue"
),
component
:
()
=>
import
(
"@/view/home/index.vue"
),
},
{
path
:
"/code_product_detail"
,
name
:
"code_product_detail"
,
component
:
()
=>
import
(
"@/view/code_product_detail/index.vue"
),
},
];
...
...
@@ -18,12 +23,16 @@ const router = createRouter({
});
// 白名单列表
const
whitelist
:
string
[]
=
[];
const
whitelist
:
string
[]
=
[
"/code_product_detail"
];
let
first
=
true
;
// 首次进入
router
.
beforeEach
(
async
(
to
,
from
,
next
)
=>
{
console
.
log
(
"router.beforeEach"
,
to
,
from
);
// 获取oss
const
OSS
=
useOssStore
();
OSS
.
getOss
();
// 是否是白名单
if
(
whitelist
.
includes
(
to
.
path
))
{
next
();
...
...
@@ -31,9 +40,6 @@ router.beforeEach(async (to, from, next) => {
}
if
(
first
)
{
const
USER
=
useUserStore
();
const
OSS
=
useOssStore
();
// 获取token 登陆
let
token
=
getUrlKey
(
"token"
)
||
getToken
();
if
(
!
token
)
{
...
...
@@ -42,8 +48,8 @@ router.beforeEach(async (to, from, next) => {
}
setToken
(
token
);
const
USER
=
useUserStore
();
await
USER
.
getUserInfo
(
token
);
// 获取用户信息
OSS
.
getOss
();
// 获取oss
first
=
false
;
}
...
...
src/utils/container/index.ts
View file @
5f2970ea
import
{
getUrlKey
}
from
"../public"
;
import
*
as
dd_util
from
"./dd"
;
import
*
as
wx_util
from
"./wx"
;
...
...
@@ -14,6 +15,7 @@ export const container = {
browser
:
!
openContainer
.
includes
(
"dingtalk"
)
&&
!
openContainer
.
includes
(
"wxwork"
),
qr_code
:
!!
getUrlKey
(
"external_token"
),
};
// 登陆
...
...
src/utils/directive.ts
View file @
5f2970ea
...
...
@@ -12,7 +12,7 @@ export const signature = (el: any, binding: DirectiveBinding<any>) => {
image_info
=
{
src
:
binding
.
value
};
}
if
(
image_info
.
src
)
{
if
(
image_info
?
.
src
)
{
// 要操作的属性 src图片url bg背景图
const
type
=
binding
.
arg
||
"src"
;
...
...
src/utils/hook.ts
View file @
5f2970ea
import
{
ref
,
ComputedRef
}
from
"vue"
;
import
{
showToast
}
from
"vant"
;
import
{
throttle
}
from
"./public"
;
import
api
from
"@/api"
;
import
{
UserInfo
}
from
"@/components/select_user_dept/hook"
;
import
{
$toast
}
from
"@/components/toast"
;
// 数据列表
interface
UseDataList
{
...
...
@@ -31,7 +31,10 @@ export const useDataList = (props: ComputedRef<UseDataList>) => {
const
deleteData
=
async
(
params
:
any
)
=>
{
const
msg
=
await
props
.
value
?.
deleteApi
?.(
params
);
if
(
msg
?.
code
==
0
)
{
showToast
({
message
:
"删除成功"
,
position
:
"top"
});
$toast
({
type
:
"warning"
,
message
:
"删除成功"
,
});
getList
();
}
};
...
...
src/utils/public.ts
View file @
5f2970ea
import
{
useOssStore
}
from
"@/store/oss"
;
import
axios
from
"axios"
;
import
file_icon
from
"@/assets/file_icon/index"
;
import
{
showToast
}
from
"van
t"
;
import
{
$toast
}
from
"@/components/toas
t"
;
/**
* 格式化文件大小
...
...
@@ -140,15 +140,18 @@ export const uploadFile = (props: UploadFile) => {
// 验证文件格式
const
ext
=
file
.
name
.
split
(
"."
).
pop
().
toLowerCase
();
if
(
props
.
ext
&&
!
props
.
ext
.
includes
(
ext
))
{
showToast
({
message
:
"请上传正确格式的文件"
,
position
:
"top"
});
$toast
({
type
:
"warning"
,
message
:
"请上传正确格式的文件"
,
});
return
;
}
// 验证文件大小
if
(
file
.
size
>
(
props
?.
size
||
5
)
*
1024
*
1024
)
{
showToast
({
$toast
({
type
:
"warning"
,
message
:
`文件大小不能超过
${
props
.
size
}
MB`
,
position
:
"top"
,
});
return
;
}
...
...
src/utils/useTag.ts
View file @
5f2970ea
import
{
ref
}
from
"vue"
;
import
api
from
"@/api"
;
import
{
showToast
}
from
"van
t"
;
import
{
$toast
}
from
"@/components/toas
t"
;
export
const
user_tag_colors
=
[
"#02B092"
,
"#F75E5E"
,
"#BC84CC"
,
"#6AB5CE"
,
"#F59820"
,
"#FF8E6B"
,
"#A1887F"
,
"#C4CB62"
,
"#1679FC"
,
"#F75E8D"
,
"#5C6BC0"
,
"#9A88B8"
,
"#5DC8F6"
,
"#626262"
,
"#02B092"
,
"#F75E5E"
,
"#BC84CC"
,
"#6AB5CE"
,
"#F59820"
,
"#FF8E6B"
,
"#A1887F"
,
"#C4CB62"
,
"#1679FC"
,
"#F75E8D"
,
"#5C6BC0"
,
"#9A88B8"
,
"#5DC8F6"
,
"#626262"
,
];
// 客户标签列表
export
interface
UserTag
{
id
:
string
;
label_color
:
string
;
label_icon
:
string
;
label_name
:
string
;
label_type
:
string
;
id
:
string
;
label_color
:
string
;
label_icon
:
string
;
label_name
:
string
;
label_type
:
string
;
}
export
interface
AddTag
{
label_color
:
string
;
label_icon
:
string
;
label_name
:
string
;
label_color
:
string
;
label_icon
:
string
;
label_name
:
string
;
}
export
const
useUserTag
=
()
=>
{
const
user_tags
=
ref
<
UserTag
[]
>
([]);
const
getTags
=
async
()
=>
{
const
msg
=
await
api
.
customer
.
listCustomerLabel
();
if
(
msg
.
code
==
0
)
{
user_tags
.
value
=
msg
.
data
.
data
;
}
};
getTags
();
// 客户标签统计
const
user_tags_count
=
ref
<
{
[
k
:
string
]:
number
}
>
({});
const
getUserTagsCount
=
async
(
params
:
{
form_id
:
string
})
=>
{
const
msg
=
await
api
.
customer
.
getCustomCount
(
params
);
if
(
msg
.
code
==
0
)
{
user_tags_count
.
value
=
msg
.
data
;
}
};
// 删除
const
deleteUserTag
=
async
(
id
:
string
)
=>
{
const
msg
=
await
api
.
customer
.
deleteCustomerLabel
({
ids
:
[
id
]
});
if
(
msg
.
code
==
0
)
{
showToast
({
message
:
"删除成功"
,
position
:
"top
"
});
getTags
();
}
};
// 添加
const
addUserTag
=
async
(
tag
:
AddTag
)
=>
{
if
(
!
tag
.
label_name
)
{
showToast
({
message
:
"标签名未填写"
,
position
:
"top
"
});
return
;
}
const
msg
=
await
api
.
customer
.
saveCustomLabel
(
tag
);
if
(
msg
.
code
==
0
)
{
showToast
({
message
:
"添加成功"
,
position
:
"top
"
});
getTags
();
}
};
return
{
user_tags
,
getTags
,
user_tags_count
,
getUserTagsCount
,
deleteUserTag
,
addUserTag
,
};
const
user_tags
=
ref
<
UserTag
[]
>
([]);
const
getTags
=
async
()
=>
{
const
msg
=
await
api
.
customer
.
listCustomerLabel
();
if
(
msg
.
code
==
0
)
{
user_tags
.
value
=
msg
.
data
.
data
;
}
};
getTags
();
// 客户标签统计
const
user_tags_count
=
ref
<
{
[
k
:
string
]:
number
}
>
({});
const
getUserTagsCount
=
async
(
params
:
{
form_id
:
string
})
=>
{
const
msg
=
await
api
.
customer
.
getCustomCount
(
params
);
if
(
msg
.
code
==
0
)
{
user_tags_count
.
value
=
msg
.
data
;
}
};
// 删除
const
deleteUserTag
=
async
(
id
:
string
)
=>
{
const
msg
=
await
api
.
customer
.
deleteCustomerLabel
({
ids
:
[
id
]
});
if
(
msg
.
code
==
0
)
{
$toast
({
type
:
"success"
,
message
:
"删除成功
"
});
getTags
();
}
};
// 添加
const
addUserTag
=
async
(
tag
:
AddTag
)
=>
{
if
(
!
tag
.
label_name
)
{
$toast
({
type
:
"success"
,
message
:
"标签名未填写
"
});
return
;
}
const
msg
=
await
api
.
customer
.
saveCustomLabel
(
tag
);
if
(
msg
.
code
==
0
)
{
$toast
({
type
:
"success"
,
message
:
"添加成功
"
});
getTags
();
}
};
return
{
user_tags
,
getTags
,
user_tags_count
,
getUserTagsCount
,
deleteUserTag
,
addUserTag
,
};
};
// 产品分类
export
interface
ProductClass
{
classify_name
:
string
;
id
:
string
;
_parent_id
?:
string
;
edit
?:
boolean
;
classify_name
:
string
;
id
:
string
;
_parent_id
?:
string
;
edit
?:
boolean
;
}
export
const
useProductClass
=
()
=>
{
const
product_class
=
ref
<
ProductClass
[]
>
([]);
const
getProductClass
=
async
()
=>
{
const
msg
=
await
api
.
product
.
listClassify
({});
if
(
msg
.
code
==
0
)
{
product_class
.
value
=
msg
.
data
.
data
;
}
};
getProductClass
();
// 获取树形子节点
const
getChildren
=
(
id
:
string
):
ProductClass
[]
=>
{
const
children
=
product_class
.
value
.
filter
((
it
)
=>
it
.
_parent_id
==
id
);
if
(
!
children
.
length
)
return
[];
return
children
.
map
((
it
)
=>
({
...
it
,
children
:
getChildren
(
it
.
id
),
}));
};
// 产品分类统计
const
product_class_count
=
ref
<
{
[
k
:
string
]:
number
}
>
({});
const
getProductClassCount
=
async
(
params
:
{
form_id
:
string
})
=>
{
const
msg
=
await
api
.
product
.
getClassifyCount
(
params
);
if
(
msg
.
code
==
0
)
{
product_class_count
.
value
=
msg
.
data
;
}
};
// 删除产品分类
const
deleteClassify
=
async
(
id
:
string
)
=>
{
const
msg
=
await
api
.
product
.
deleteClassify
({
ids
:
[
id
],
});
if
(
msg
.
code
==
0
)
{
showToast
({
message
:
"删除成功"
,
position
:
"top"
});
getProductClass
();
}
};
// 添加产品分类
const
addClassify
=
async
(
data
:
ProductClass
)
=>
{
const
msg
=
await
api
.
product
.
saveClassify
({
id
:
data
.
id
==
"edit"
?
undefined
:
data
.
id
,
classify_name
:
data
.
classify_name
,
_parent_id
:
data
.
_parent_id
,
});
if
(
msg
.
code
==
0
)
{
showToast
({
message
:
`
${
data
.
id
?
"添加"
:
"修改"
}
成功`
,
position
:
"top"
});
getProductClass
();
}
};
return
{
product_class
,
getProductClass
,
getChildren
,
product_class_count
,
getProductClassCount
,
deleteClassify
,
addClassify
,
};
const
product_class
=
ref
<
ProductClass
[]
>
([]);
const
getProductClass
=
async
()
=>
{
const
msg
=
await
api
.
product
.
listClassify
({});
if
(
msg
.
code
==
0
)
{
product_class
.
value
=
msg
.
data
.
data
;
}
};
getProductClass
();
// 获取树形子节点
const
getChildren
=
(
id
:
string
):
ProductClass
[]
=>
{
const
children
=
product_class
.
value
.
filter
(
(
it
)
=>
it
.
_parent_id
==
id
);
if
(
!
children
.
length
)
return
[];
return
children
.
map
((
it
)
=>
({
...
it
,
children
:
getChildren
(
it
.
id
),
}));
};
// 产品分类统计
const
product_class_count
=
ref
<
{
[
k
:
string
]:
number
}
>
({});
const
getProductClassCount
=
async
(
params
:
{
form_id
:
string
})
=>
{
const
msg
=
await
api
.
product
.
getClassifyCount
(
params
);
if
(
msg
.
code
==
0
)
{
product_class_count
.
value
=
msg
.
data
;
}
};
// 删除产品分类
const
deleteClassify
=
async
(
id
:
string
)
=>
{
const
msg
=
await
api
.
product
.
deleteClassify
({
ids
:
[
id
],
});
if
(
msg
.
code
==
0
)
{
$toast
({
type
:
"success"
,
message
:
"删除成功"
});
getProductClass
();
}
};
// 添加产品分类
const
addClassify
=
async
(
data
:
ProductClass
)
=>
{
const
msg
=
await
api
.
product
.
saveClassify
({
id
:
data
.
id
==
"edit"
?
undefined
:
data
.
id
,
classify_name
:
data
.
classify_name
,
_parent_id
:
data
.
_parent_id
,
});
if
(
msg
.
code
==
0
)
{
$toast
({
type
:
"success"
,
message
:
`
${
data
.
id
?
"添加"
:
"修改"
}
成功`
,
});
getProductClass
();
}
};
return
{
product_class
,
getProductClass
,
getChildren
,
product_class_count
,
getProductClassCount
,
deleteClassify
,
addClassify
,
};
};
// 工单标签
export
interface
OrderTag
extends
UserTag
{}
export
const
useOrderTag
=
()
=>
{
const
order_tag
=
ref
<
OrderTag
[]
>
([]);
const
getOrderTag
=
async
()
=>
{
const
msg
=
await
api
.
order
.
listOrderLabel
({
page
:
1
,
size
:
1000
,
});
if
(
msg
.
code
==
0
)
{
order_tag
.
value
=
msg
.
data
.
data
;
console
.
log
(
"order_tag"
,
order_tag
.
value
);
}
};
getOrderTag
();
return
{
order_tag
,
getOrderTag
,
};
const
order_tag
=
ref
<
OrderTag
[]
>
([]);
const
getOrderTag
=
async
()
=>
{
const
msg
=
await
api
.
order
.
listOrderLabel
({
page
:
1
,
size
:
1000
,
});
if
(
msg
.
code
==
0
)
{
order_tag
.
value
=
msg
.
data
.
data
;
console
.
log
(
"order_tag"
,
order_tag
.
value
);
}
};
getOrderTag
();
return
{
order_tag
,
getOrderTag
,
};
};
// 工作状态
export
interface
WorkStatusTag
{
id
:
string
;
color
:
string
;
is_system
:
"0"
|
"1"
;
name
:
string
;
id
:
string
;
color
:
string
;
is_system
:
"0"
|
"1"
;
name
:
string
;
}
export
const
useWorkStatusTag
=
()
=>
{
const
work_status_tag
=
ref
<
WorkStatusTag
[]
>
([]);
const
getWorkStatusTag
=
async
()
=>
{
const
msg
=
await
api
.
user
.
listWorkStatus
();
if
(
msg
.
code
==
0
)
{
work_status_tag
.
value
=
msg
.
data
.
data
;
}
};
// 添加
const
addWorkStatusTag
=
async
(
data
:
{
name
:
string
;
color
:
string
})
=>
{
const
msg
=
await
api
.
user
.
addWorkStatus
(
data
);
if
(
msg
.
code
==
0
)
{
showToast
({
message
:
"添加成功"
,
position
:
"top
"
});
getWorkStatusTag
();
}
};
// 删除
const
delWorkStatusTag
=
async
(
id
:
string
)
=>
{
const
msg
=
await
api
.
user
.
deleteWorkStatus
({
ids
:
[
id
]
});
if
(
msg
.
code
==
0
)
{
showToast
({
message
:
"删除成功"
,
position
:
"top
"
});
getWorkStatusTag
();
}
};
return
{
work_status_tag
,
getWorkStatusTag
,
addWorkStatusTag
,
delWorkStatusTag
,
};
const
work_status_tag
=
ref
<
WorkStatusTag
[]
>
([]);
const
getWorkStatusTag
=
async
()
=>
{
const
msg
=
await
api
.
user
.
listWorkStatus
();
if
(
msg
.
code
==
0
)
{
work_status_tag
.
value
=
msg
.
data
.
data
;
}
};
// 添加
const
addWorkStatusTag
=
async
(
data
:
{
name
:
string
;
color
:
string
})
=>
{
const
msg
=
await
api
.
user
.
addWorkStatus
(
data
);
if
(
msg
.
code
==
0
)
{
$toast
({
type
:
"success"
,
message
:
"添加成功
"
});
getWorkStatusTag
();
}
};
// 删除
const
delWorkStatusTag
=
async
(
id
:
string
)
=>
{
const
msg
=
await
api
.
user
.
deleteWorkStatus
({
ids
:
[
id
]
});
if
(
msg
.
code
==
0
)
{
$toast
({
type
:
"success"
,
message
:
"删除成功
"
});
getWorkStatusTag
();
}
};
return
{
work_status_tag
,
getWorkStatusTag
,
addWorkStatusTag
,
delWorkStatusTag
,
};
};
src/view/code_product_detail/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<div
class=
"code_product_detail"
>
<img
class=
"background_img"
v-signature:src=
"detail_config?.back_ground?.file_url"
/>
<div
class=
"detail"
>
<div
class=
"order"
v-if=
"product_detail.have_dealing_order"
:style=
"
{ background: hexToRgba('#f5a034', 0.15) }">
<i
class=
"icon-131"
></i>
<span>
当前产品已有
{{
product_detail
.
have_dealing_order
}}
个工单正在处理中!
</span>
<i
class=
"icon-46"
></i>
</div>
<span
class=
"title"
v-if=
"detail_config?.show_customer_fields?.length"
>
客户信息
</span>
<div
class=
"detail_item"
v-if=
"getShowCusmtomerFields('customer_name')"
>
<span
class=
"label"
>
客户名称
</span>
<span>
{{
product_detail
?.
customer_info
?.
customer_name
}}
</span>
</div>
<div
class=
"detail_item"
v-if=
"getShowCusmtomerFields('link_user_name')"
>
<span
class=
"label"
>
联系人
</span>
<span>
{{
product_detail
?.
customer_info
?.
link_user_name
}}
</span>
</div>
<div
class=
"detail_item"
v-if=
"getShowCusmtomerFields('link_user_phone')"
>
<span
class=
"label"
>
联系电话
</span>
<span>
{{
product_detail
?.
customer_info
?.
link_user_phone
}}
</span>
</div>
<div
class=
"detail_item"
v-if=
"getShowCusmtomerFields('link_addr')"
>
<span
class=
"label"
>
联系地址
</span>
<span>
{{
product_detail
?.
customer_info
?.
link_addr
?.
address
||
"--"
}}
</span>
</div>
<div
class=
"detail_item"
v-if=
"getShowCusmtomerFields('link_addr')"
>
<span
class=
"label"
>
详细地址
</span>
<span>
{{
product_detail
?.
customer_info
?.
link_addr
?.
detailed_address
||
"--"
}}
</span>
</div>
<span
class=
"title"
v-if=
"detail_config?.show_product_fields?.length"
>
产品信息
</span>
<div
class=
"detail_item"
v-if=
"getShowProductFields('product_name')"
>
<span
class=
"label"
>
产品名称
</span>
<span>
{{
product_detail
?.
product_info
?.
product_name
}}
</span>
</div>
<div
class=
"detail_item"
v-if=
"getShowProductFields('product_number')"
>
<span
class=
"label"
>
产品编号
</span>
<span>
{{
product_detail
?.
product_info
?.
product_number
}}
</span>
</div>
<div
class=
"detail_item"
v-if=
"getShowProductFields('product_type')"
>
<span
class=
"label"
>
规格型号
</span>
<span>
{{
product_detail
?.
product_info
?.
product_type
}}
</span>
</div>
<div
class=
"detail_item"
v-if=
"getShowProductFields('product_images')"
>
<span
class=
"label"
>
产品照片
</span>
<div
class=
"images"
>
<img
v-for=
"it in product_detail?.product_info
?.product_images || []"
v-signature:src=
"it.file_url"
/>
</div>
</div>
</div>
<div
class=
"operate bottom_button"
>
<span
class=
"button plain"
v-for=
"it in detail_config.buttons || []"
@
click=
"createFormData(it)"
>
{{
it
.
button_name
}}
</span>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
"vue"
;
import
api
from
"@/api"
;
import
{
File
,
hexToRgba
}
from
"@/utils/public"
;
import
{
$createFormData
}
from
"@/components/create_form_data/index"
;
// 产品详情
const
product_detail
=
ref
<
{
customer_info
:
any
;
have_dealing_order
:
number
;
product_info
:
any
;
}
>
({}
as
any
);
const
getProductDetail
=
async
()
=>
{
const
msg
=
await
api
.
external
.
getInfoByQrCode
();
if
(
msg
.
code
==
0
)
{
product_detail
.
value
=
msg
.
data
;
console
.
log
(
"product_detail"
,
product_detail
.
value
);
}
};
// 显示配置
interface
QrCodeConfig
{
form_id
:
string
;
back_ground
:
File
|
null
;
show_customer_fields
:
string
[];
show_product_fields
:
string
[];
buttons
:
{
button_name
:
string
;
order_form_id
:
string
;
button_order_type
:
string
;
}[];
[
k
:
string
]:
any
;
}
const
detail_config
=
ref
<
QrCodeConfig
>
({}
as
QrCodeConfig
);
const
getDetailConfig
=
async
()
=>
{
const
msg
=
await
api
.
external
.
getQrCodeWindow
();
if
(
msg
.
code
==
0
)
{
detail_config
.
value
=
msg
.
data
;
console
.
log
(
"detail_config"
,
detail_config
.
value
);
}
};
const
getShowCusmtomerFields
=
(
id
:
string
)
=>
{
return
(
detail_config
.
value
.
show_customer_fields
||
[]).
includes
(
id
);
};
const
getShowProductFields
=
(
id
:
string
)
=>
{
return
(
detail_config
.
value
.
show_product_fields
||
[]).
includes
(
id
);
};
// 创建
const
createFormData
=
(
button
:
any
)
=>
{
$createFormData
({
form_id
:
button
.
order_form_id
,
});
};
const
init
=
()
=>
{
getProductDetail
();
getDetailConfig
();
};
init
();
</
script
>
<
style
lang=
"less"
scoped
>
.code_product_detail {
width: 100vw;
height: 100vh;
overflow: auto;
box-sizing: border-box;
position: relative;
.background_img {
width: 100%;
position: fixed;
}
.detail {
width: calc(100% - 40px);
box-sizing: border-box;
margin: 0 20px;
margin-top: 140px;
background: white;
position: relative;
border-top-left-radius: 14px;
border-top-right-radius: 14px;
padding: 14px;
min-height: calc(100vh - 140px);
box-sizing: border-box;
display: flex;
flex-direction: column;
box-shadow: 0 -2px 4px 1px #f3f3f3;
.order {
display: flex;
align-items: center;
color: var(--orange);
background-color: red;
padding: 10px;
border-radius: 6px;
span {
flex: 1;
margin: 0 6px;
}
}
.title {
position: relative;
font-weight: bold;
margin-left: 10px;
margin-top: 20px;
&::after {
content: "";
position: absolute;
left: -10px;
top: 0;
bottom: 0;
width: 4px;
background-color: var(--blue);
}
}
.detail_item {
margin-top: 20px;
margin-left: 10px;
display: flex;
.label {
width: 90px;
flex-shrink: 0;
}
span {
word-break: break-all;
}
.images {
display: flex;
flex-wrap: wrap;
img {
width: 70px;
height: 70px;
border-radius: 10px;
border: 1px dotted var(--icon_gray);
margin: 0 8px 8px 0;
}
}
}
}
.operate {
position: fixed;
bottom: 0;
width: 100%;
padding: 10px 30px;
box-sizing: border-box;
z-index: 1;
display: flex;
justify-content: center;
.button {
max-width: 50%;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
}
}
}
</
style
>
src/view/home/index.vue
0 → 100644
View file @
5f2970ea
<
template
>
<div>
123123231
</div>
</
template
>
<
script
setup
lang=
"ts"
></
script
>
<
style
lang=
"less"
scoped
></
style
>
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