Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
basic-uniapp-v3
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
1
合并请求
1
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Basic
basic-uniapp-v3
Commits
4a6f63f4
提交
4a6f63f4
authored
3月 30, 2026
作者:
廖在望
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: app样式调整和增加验证码登录
上级
17c12bdd
隐藏空白字符变更
内嵌
并排
正在显示
22 个修改的文件
包含
1417 行增加
和
878 行删除
+1417
-878
nongchang.ts
src/api/model/nongchang.ts
+8
-0
SlideVerify.vue
src/components/slide-verify/SlideVerify.vue
+211
-0
caixianyi.vue
src/pages/chanxiao/caixianyi/caixianyi.vue
+1
-1
chanxiao.vue
src/pages/chanxiao/chanxiao.vue
+1
-1
hema.vue
src/pages/chanxiao/hema/hema.vue
+1
-1
hongxing.vue
src/pages/chanxiao/hongxing/hongxing.vue
+1
-1
qiandama.vue
src/pages/chanxiao/qiandama/qiandama.vue
+1
-1
xiaoxiangchaoshi.vue
src/pages/chanxiao/xiaoxiangchaoshi/xiaoxiangchaoshi.vue
+1
-1
zhusiling.vue
src/pages/chanxiao/zhusiling/zhusiling.vue
+1
-1
device.vue
src/pages/device/device.vue
+1
-1
fuwu.vue
src/pages/fuwu/fuwu.vue
+6
-6
login.vue
src/pages/login/login.vue
+490
-505
register.vue
src/pages/login/register.vue
+423
-328
index.vue
src/pages/mine/index.vue
+1
-1
nongchang.vue
src/pages/nongchang/nongchang.vue
+2
-2
apply-dialog.vue
src/pages/nongjifuwu/components/apply-dialog.vue
+226
-23
nongyedamoxing.vue
src/pages/nongyedamoxing/nongyedamoxing.vue
+2
-2
nongzhi.vue
src/pages/nongzhi/nongzhi.vue
+1
-1
shouye.vue
src/pages/shouye/shouye.vue
+1
-1
wode.vue
src/pages/wode/wode.vue
+1
-1
user.ts
src/store/modules/user.ts
+36
-0
components.d.ts
types/components.d.ts
+1
-0
没有找到文件。
src/api/model/nongchang.ts
浏览文件 @
4a6f63f4
...
...
@@ -100,6 +100,14 @@ export function editFarmbase(params = {}) {
params
,
})
}
export
function
getFarmbaseDetail
(
params
:
any
=
{})
{
return
getFarmbaseInfoById
(
params
)
}
export
function
saveFarmbase
(
params
:
any
=
{})
{
return
params
?.
id
?
editFarmbase
(
params
)
:
addFarmbase
(
params
)
}
/**
* 基地列表
* @param {any} params
...
...
src/components/slide-verify/SlideVerify.vue
0 → 100644
浏览文件 @
4a6f63f4
<
script
setup
lang=
"ts"
>
/**
* 滑动验证组件
* 用法:<SlideVerify v-model:show="showSlide" @success="onVerifySuccess" />
*/
const
props
=
defineProps
<
{
show
:
boolean
}
>
()
const
emit
=
defineEmits
<
{
(
e
:
'update:show'
,
val
:
boolean
):
void
(
e
:
'success'
):
void
(
e
:
'close'
):
void
}
>
()
const
TRACK_WIDTH
=
560
// rpx,滑道宽度(与样式保持一致)
const
BTN_WIDTH
=
96
// rpx,滑块宽度
const
maxRpx
=
TRACK_WIDTH
-
BTN_WIDTH
// 最大可拖动距离(rpx)
const
state
=
reactive
({
translateX
:
0
,
// 当前滑块位移(px,运行时换算)
isDragging
:
false
,
startX
:
0
,
maxPx
:
0
,
// 运行时根据屏幕宽度换算
verified
:
false
,
text
:
'按住滑块,拖动到最右侧'
,
})
// 将 rpx 换算为 px
function
rpxToPx
(
rpx
:
number
):
number
{
const
screenWidth
=
uni
.
getSystemInfoSync
().
windowWidth
return
(
rpx
/
750
)
*
screenWidth
}
watch
(()
=>
props
.
show
,
(
val
)
=>
{
if
(
val
)
{
reset
()
state
.
maxPx
=
rpxToPx
(
maxRpx
)
}
})
function
reset
()
{
state
.
translateX
=
0
state
.
isDragging
=
false
state
.
verified
=
false
state
.
text
=
'按住滑块,拖动到最右侧'
}
function
onTouchStart
(
e
:
any
)
{
if
(
state
.
verified
)
return
state
.
isDragging
=
true
state
.
startX
=
e
.
touches
[
0
].
clientX
}
function
onTouchMove
(
e
:
any
)
{
if
(
!
state
.
isDragging
||
state
.
verified
)
return
const
dx
=
e
.
touches
[
0
].
clientX
-
state
.
startX
state
.
translateX
=
Math
.
max
(
0
,
Math
.
min
(
dx
,
state
.
maxPx
))
}
function
onTouchEnd
()
{
if
(
!
state
.
isDragging
)
return
state
.
isDragging
=
false
// 距终点 10px 以内视为验证通过
if
(
state
.
translateX
>=
state
.
maxPx
-
rpxToPx
(
10
))
{
state
.
translateX
=
state
.
maxPx
state
.
verified
=
true
state
.
text
=
'验证通过'
setTimeout
(()
=>
{
emit
(
'success'
)
close
()
},
600
)
}
else
{
// 未拖到底,回弹
state
.
translateX
=
0
}
}
function
close
()
{
emit
(
'update:show'
,
false
)
emit
(
'close'
)
reset
()
}
</
script
>
<
template
>
<view
class=
"sv-mask"
v-if=
"show"
@
click
.
self=
"close"
>
<view
class=
"sv-panel"
>
<view
class=
"sv-title"
>
安全验证
</view>
<view
class=
"sv-desc"
>
{{
state
.
text
}}
</view>
<!-- 滑道 -->
<view
class=
"sv-track"
>
<!-- 已划过的高亮区 -->
<view
class=
"sv-fill"
:style=
"
{ width: state.translateX + 'px' }" />
<!-- 滑块 -->
<view
class=
"sv-btn"
:class=
"
{ success: state.verified }"
:style="{ transform: `translateX(${state.translateX}px)` }"
@touchstart.prevent="onTouchStart"
@touchmove.prevent="onTouchMove"
@touchend.prevent="onTouchEnd"
>
<text
class=
"sv-arrow"
v-if=
"!state.verified"
>
→
</text>
<text
class=
"sv-check"
v-else
"
>
✓
</text>
</view>
</view>
<view
class=
"sv-close"
@
click=
"close"
>
<text>
取消
</text>
</view>
</view>
</view>
</
template
>
<
style
lang=
"scss"
scoped
>
.sv-mask
{
position
:
fixed
;
inset
:
0
;
background
:
rgba
(
0
,
0
,
0
,
0.45
);
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
z-index
:
9999
;
}
.sv-panel
{
width
:
640
rpx
;
background
:
#fff
;
border-radius
:
24
rpx
;
padding
:
48
rpx
40
rpx
40
rpx
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
box-shadow
:
0
16
rpx
48
rpx
rgba
(
0
,
0
,
0
,
0.16
);
}
.sv-title
{
font-size
:
34
rpx
;
font-weight
:
700
;
color
:
#222
;
margin-bottom
:
12
rpx
;
}
.sv-desc
{
font-size
:
26
rpx
;
color
:
#888
;
margin-bottom
:
40
rpx
;
}
.sv-track
{
position
:
relative
;
width
:
560
rpx
;
height
:
96
rpx
;
background
:
#f0f0f0
;
border-radius
:
48
rpx
;
overflow
:
hidden
;
}
.sv-fill
{
position
:
absolute
;
left
:
0
;
top
:
0
;
height
:
100%
;
background
:
linear-gradient
(
90deg
,
#b8e8c0
,
#5db66f
);
border-radius
:
48
rpx
0
0
48
rpx
;
transition
:
width
0.05s
;
}
.sv-btn
{
position
:
absolute
;
left
:
0
;
top
:
0
;
width
:
96
rpx
;
height
:
96
rpx
;
background
:
#fff
;
border-radius
:
48
rpx
;
box-shadow
:
0
4
rpx
16
rpx
rgba
(
0
,
0
,
0
,
0.18
);
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
transition
:
background
0.3s
;
z-index
:
2
;
&.success
{
background
:
#5db66f
;
}
}
.sv-arrow
{
font-size
:
40
rpx
;
color
:
#5db66f
;
font-weight
:
700
;
}
.sv-check
{
font-size
:
40
rpx
;
color
:
#fff
;
font-weight
:
700
;
}
.sv-close
{
margin-top
:
32
rpx
;
text
{
font-size
:
28
rpx
;
color
:
#aaa
;
}
}
</
style
>
src/pages/chanxiao/caixianyi/caixianyi.vue
浏览文件 @
4a6f63f4
...
...
@@ -141,7 +141,7 @@
overflow-x
:
hidden
;
.section
{
padding
:
28
rpx
38
rpx
382
rpx
;
background-image
:
url('/static/images/codefun/05019411cdb9383e51ab8923569568df.png')
;
background-image
:
url('
../../..
/static/images/codefun/05019411cdb9383e51ab8923569568df.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/chanxiao/chanxiao.vue
浏览文件 @
4a6f63f4
...
...
@@ -465,7 +465,7 @@
padding-bottom
:
32
rpx
;
.section
{
padding
:
0
28
rpx
8
rpx
;
background-image
:
url('/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-image
:
url('
../..
/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/chanxiao/hema/hema.vue
浏览文件 @
4a6f63f4
...
...
@@ -163,7 +163,7 @@
overflow-x
:
hidden
;
.section
{
padding
:
28
rpx
38
rpx
382
rpx
;
background-image
:
url('/static/images/codefun/7f3f04389e8c4f41e9cf97f290ffa85d.png')
;
background-image
:
url('
../../..
/static/images/codefun/7f3f04389e8c4f41e9cf97f290ffa85d.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/chanxiao/hongxing/hongxing.vue
浏览文件 @
4a6f63f4
...
...
@@ -186,7 +186,7 @@
overflow-x
:
hidden
;
.section
{
padding
:
28
rpx
42
rpx
382
rpx
;
background-image
:
url('/static/images/codefun/319caa972700f74982472280d1edb65e.png')
;
background-image
:
url('
../../..
/static/images/codefun/319caa972700f74982472280d1edb65e.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/chanxiao/qiandama/qiandama.vue
浏览文件 @
4a6f63f4
...
...
@@ -146,7 +146,7 @@
overflow-x
:
hidden
;
.section
{
padding
:
28
rpx
38
rpx
382
rpx
;
background-image
:
url('/static/images/codefun/ad85a97458671a968e690f0108861184.png')
;
background-image
:
url('
../../..
/static/images/codefun/ad85a97458671a968e690f0108861184.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/chanxiao/xiaoxiangchaoshi/xiaoxiangchaoshi.vue
浏览文件 @
4a6f63f4
...
...
@@ -161,7 +161,7 @@
overflow-x
:
hidden
;
.section
{
padding
:
28
rpx
38
rpx
382
rpx
;
background-image
:
url('/static/images/codefun/d64598e1f76d3e1977d820f8b2f02843.png')
;
background-image
:
url('
../../..
/static/images/codefun/d64598e1f76d3e1977d820f8b2f02843.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/chanxiao/zhusiling/zhusiling.vue
浏览文件 @
4a6f63f4
...
...
@@ -153,7 +153,7 @@
overflow-x
:
hidden
;
.section
{
padding
:
28
rpx
38
rpx
382
rpx
;
background-image
:
url('/static/images/codefun/b5493813a21cb38af4c6d8314e21b88c.png')
;
background-image
:
url('
../../..
/static/images/codefun/b5493813a21cb38af4c6d8314e21b88c.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/device/device.vue
浏览文件 @
4a6f63f4
...
...
@@ -498,7 +498,7 @@ return 'status-disconnected'
.section_5
{
padding
:
12
rpx
0
116
rpx
;
background-image
:
url('/static/images/codefun/4be80e2618f3c4b4aa1ce64fd9063abf.png')
;
background-image
:
url('
../..
/static/images/codefun/4be80e2618f3c4b4aa1ce64fd9063abf.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
width
:
160
rpx
;
...
...
src/pages/fuwu/fuwu.vue
浏览文件 @
4a6f63f4
...
...
@@ -805,7 +805,7 @@
.section
{
padding
:
0
28
rpx
30
rpx
28
rpx
;
background-image
:
url('/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-image
:
url('
../..
/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
@@ -1159,7 +1159,7 @@
.section_7
{
padding
:
20
rpx
0
130
rpx
;
border-radius
:
16
rpx
16
rpx
0
rpx
0
rpx
;
background-image
:
url('/static/images/codefun/6082011896d83113283c720e943a4999.png')
;
background-image
:
url('
../..
/static/images/codefun/6082011896d83113283c720e943a4999.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
}
...
...
@@ -1178,7 +1178,7 @@
.section_9
{
padding
:
20
rpx
0
130
rpx
;
border-radius
:
16
rpx
16
rpx
0
rpx
0
rpx
;
background-image
:
url('/static/images/codefun/7a4ec325d59361d6716c2ec1394550bb.png')
;
background-image
:
url('
../..
/static/images/codefun/7a4ec325d59361d6716c2ec1394550bb.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
}
...
...
@@ -1197,7 +1197,7 @@
.section_8
{
padding
:
20
rpx
0
130
rpx
;
border-radius
:
16
rpx
16
rpx
0
rpx
0
rpx
;
background-image
:
url('/static/images/codefun/e812ea5af6105a808af058cb0796b7ee.png')
;
background-image
:
url('
../..
/static/images/codefun/e812ea5af6105a808af058cb0796b7ee.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
}
...
...
@@ -1322,14 +1322,14 @@
mix-blend-mode
:
NOTTHROUGH
;
.section_15
{
padding-top
:
96
rpx
;
background-image
:
url('/static/images/codefun/7d1feb7eb973f087dcabf21b283162bc.png')
;
background-image
:
url('
../..
/static/images/codefun/7d1feb7eb973f087dcabf21b283162bc.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
height
:
200
rpx
;
}
.section_16
{
padding-top
:
96
rpx
;
background-image
:
url('/static/images/codefun/8d11e3cbcb918502bb6582a24cddb930.png')
;
background-image
:
url('
../..
/static/images/codefun/8d11e3cbcb918502bb6582a24cddb930.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
height
:
200
rpx
;
...
...
src/pages/login/login.vue
浏览文件 @
4a6f63f4
<
script
setup
lang=
"ts"
>
// import { PUSH_CLIENT_KEY } from '/@/enums/cacheEnum'
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
c
heck
Upgrade
}
from
'@/utils/upgrade'
import
{
c
loseSplashscreenAndChech
Upgrade
}
from
'@/utils/upgrade'
import
{
isDevMode
}
from
'@/utils/env'
import
Link
from
'@/utils/const/link'
import
SlideVerify
from
'@/components/slide-verify/SlideVerify.vue'
import
*
as
API
from
'/@/api/model/userInfo'
const
userStore
=
useUserStore
()
onShow
(
async
()
=>
{
// 检查是否有 token
const
token
=
userStore
.
getToken
if
(
token
)
{
model
.
isLogin
=
true
}
if
(
model
.
isLogin
)
{
// 刷新用户信息
await
userStore
.
refreshUserInfo
()
// 跳转到登录页
goHome
()
Message
.
toast
(
`欢迎回来~`
)
}
else
{
// 关闭启动页并检查更新
try
{
await
c
heck
Upgrade
()
await
c
loseSplashscreenAndChech
Upgrade
()
}
catch
(
error
)
{
console
.
log
(
'error'
,
error
)
}
finally
{
nextTick
(()
=>
{
model
.
show
=
true
})
}
}
})
// 页面数据
const
defaultText
=
'湘农数智农服'
const
readConfirmShow
=
ref
<
boolean
>
(
false
)
const
form
=
ref
()
const
showSlide
=
ref
(
false
)
const
loginMode
=
ref
(
1
)
// 1: 验证码登录, 2: 密码登录
const
slideType
=
ref
<
'sms'
|
'login'
>
(
'login'
)
const
model
=
reactive
({
show
:
false
,
isLogin
:
false
,
loading
:
false
,
text
:
defaultText
,
countdown
:
0
,
// 倒计时秒数
countdownTimer
:
null
,
// 倒计时定时器
countdown
:
0
,
form
:
{
phoneRules
:
[
{
name
:
'username'
,
rule
:
[
'required'
],
msg
:
[
'请输入手机号'
],
rule
:
[
'required'
,
'isMobile'
],
msg
:
[
'请输入手机号'
,
'请输入正确的手机号'
],
},
],
r
ules
:
[
codeLoginR
ules
:
[
{
name
:
'username'
,
rule
:
[
'required'
],
msg
:
[
'请输入
账
号'
],
rule
:
[
'required'
,
'isMobile'
],
msg
:
[
'请输入
手机号'
,
'请输入正确的手机
号'
],
},
{
name
:
'
password
'
,
name
:
'
code
'
,
rule
:
[
'required'
],
msg
:
[
'请输入
密
码'
],
msg
:
[
'请输入
验证
码'
],
},
/* {
{
name
:
'read'
,
validator
:
[
{
msg
:
'请阅读并同意服务协议和隐私政策'
,
method
:
(
value
:
boolean
)
=>
{
if
(
!
value
)
{
readConfirmShow
.
value
=
true
}
return
value
},
},
],
},
],
pwdLoginRules
:
[
{
name
:
'username'
,
rule
:
[
'required'
],
msg: ['请输入
手机
号'],
msg
:
[
'请输入
账
号'
],
},
{
name: '
code
',
name
:
'
password
'
,
rule
:
[
'required'
],
msg: ['请输入
验证
码'],
},
*/
msg
:
[
'请输入
密
码'
],
},
{
name
:
'read'
,
validator
:
[
{
msg
:
'请阅读并同意服务协议和隐私政策'
,
method
:
(
value
:
boolean
)
=>
{
readConfirmShow
.
value
=
!
value
if
(
!
value
)
{
readConfirmShow
.
value
=
true
}
return
value
},
},
...
...
@@ -92,588 +100,565 @@
data
:
{
username
:
''
,
password
:
''
,
//
code: '',
code
:
''
,
read
:
false
,
deviceId
:
''
,
},
},
})
// TODO: 开发环境快速填入账户密码,并默认勾选已读隐私政策和服务协议
let
countdownTimer
:
any
=
null
if
(
isDevMode
())
{
model
.
form
.
data
.
username
=
''
// model.form.data.code = ''
model
.
form
.
data
.
password
=
''
model
.
form
.
data
.
read
=
false
model
.
form
.
data
.
read
=
true
}
function
switchMode
(
mode
:
number
)
{
loginMode
.
value
=
mode
}
/**
* 登录
*/
function
login
()
{
form
?.
value
.
validator
(
model
.
form
.
data
,
model
.
form
.
rules
).
then
(
async
(
res
:
{
isPassed
:
boolean
})
=>
{
const
rules
=
loginMode
.
value
===
1
?
model
.
form
.
codeLoginRules
:
model
.
form
.
pwdLoginRules
form
?.
value
.
validator
(
model
.
form
.
data
,
rules
).
then
(
async
(
res
:
{
isPassed
:
boolean
})
=>
{
if
(
res
.
isPassed
)
{
// 登录参数
/* const params = {
mobile: model.form.data.username,
captcha: model.form.data.code,
} */
const
params
=
{
username
:
model
.
form
.
data
.
username
,
password
:
model
.
form
.
data
.
password
,
}
// 短信登录
model
.
loading
=
true
// API.phoneLogin(params)
API
.
sysLogin
(
params
)
.
then
(
async
(
body
)
=>
{
console
.
log
(
'body'
,
body
)
if
(
body
)
{
// 设置登录缓存信息
userStore
.
setToken
(
body
.
token
)
// 登录成功后查询用户信息
const
user
=
await
API
.
getUserInfo
()
userStore
.
setUserInfo
(
user
)
// 打开登录页
goHome
()
Message
.
toast
(
`登录成功, 欢迎回来~`
)
}
else
{
Message
.
toast
(
body
.
message
)
return
false
}
})
.
finally
(()
=>
{
model
.
loading
=
false
})
// 登录前先做滑动验证 (Requirement 7)
slideType
.
value
=
'login'
showSlide
.
value
=
true
}
})
}
/**
* 获取验证码
*/
function
smsCode
()
{
form
?.
value
.
validator
(
model
.
form
.
data
,
model
.
form
.
phoneRules
).
then
(
async
(
res
:
{
isPassed
:
boolean
})
=>
{
if
(
res
.
isPassed
)
{
// 如果已经在倒计时中,不重复发送
if
(
model
.
countdown
>
0
)
{
return
}
if
(
model
.
countdown
>
0
)
return
slideType
.
value
=
'sms'
showSlide
.
value
=
true
}
})
}
const
params
=
{
/* mobile: model.form.data.username,
smsmode: '0', */
function
onSlideSuccess
()
{
if
(
slideType
.
value
===
'sms'
)
{
// 发送验证码
const
params
=
{
mobile
:
model
.
form
.
data
.
username
,
smsmode
:
'1'
,
}
API
.
sysSms
(
params
)
.
then
(
async
()
=>
{
Message
.
toast
(
'验证码已发送'
)
startCountdown
()
})
.
catch
(()
=>
{
Message
.
toast
(
'验证码发送失败'
)
})
}
else
{
// 执行登录
doLogin
()
}
}
mobile
:
model
.
form
.
data
.
username
,
smsmode
:
1
,
}
API
.
sysSms
(
params
)
.
then
(
async
(
body
)
=>
{
Message
.
toast
(
'验证码已发送'
)
console
.
log
(
'body'
,
body
)
// 开始倒计时
startCountdown
()
})
.
catch
(()
=>
{
// 即使请求失败也显示倒计时,防止重复点击
startCountdown
()
})
function
doLogin
()
{
model
.
loading
=
true
if
(
loginMode
.
value
===
1
)
{
// 验证码登录
const
params
=
{
mobile
:
model
.
form
.
data
.
username
,
captcha
:
model
.
form
.
data
.
code
,
}
})
API
.
phoneLogin
(
params
)
.
then
(
handleLoginSuccess
)
.
finally
(()
=>
(
model
.
loading
=
false
))
}
else
{
// 密码登录
const
params
=
{
username
:
model
.
form
.
data
.
username
,
password
:
model
.
form
.
data
.
password
,
}
API
.
sysLogin
(
params
)
.
then
(
handleLoginSuccess
)
.
finally
(()
=>
(
model
.
loading
=
false
))
}
}
async
function
handleLoginSuccess
(
body
:
any
)
{
if
(
body
&&
body
.
token
)
{
userStore
.
setToken
(
body
.
token
)
const
user
=
await
API
.
getUserInfo
()
userStore
.
setUserInfo
(
user
)
goHome
()
Message
.
toast
(
`登录成功, 欢迎回来~`
)
}
else
{
Message
.
toast
(
body
.
message
||
'登录失败'
)
}
}
/**
* 开始倒计时
*/
function
startCountdown
()
{
if
(
countdownTimer
)
clearInterval
(
countdownTimer
)
model
.
countdown
=
60
model
.
countdownTimer
=
setInterval
(()
=>
{
countdownTimer
=
setInterval
(()
=>
{
model
.
countdown
--
if
(
model
.
countdown
<=
0
)
{
clearInterval
(
model
.
countdownTimer
)
model
.
countdownTimer
=
null
clearInterval
(
countdownTimer
)
countdownTimer
=
null
}
},
1000
)
}
/**
* 跳转到门户页
*/
function
goHome
()
{
uni
.
reLaunch
({
url
:
'/pages/shouye/shouye'
,
})
uni
.
reLaunch
({
url
:
'/pages/shouye/shouye'
})
}
function
goRegister
()
{
uni
.
navigateTo
({
url
:
'/pages/login/register'
})
}
onHide
(()
=>
{
model
.
show
=
false
if
(
countdownTimer
)
{
clearInterval
(
countdownTimer
)
countdownTimer
=
null
}
})
function
handleConfirm
()
{
model
.
form
.
data
.
read
=
true
login
()
readConfirmShow
.
value
=
false
}
onUnload
(()
=>
{
if
(
countdownTimer
)
{
clearInterval
(
countdownTimer
)
countdownTimer
=
null
}
})
function
handleCancel
()
{
model
.
form
.
data
.
read
=
false
function
onReadConfirm
(
val
:
any
)
{
if
(
val
.
index
===
0
)
{
model
.
form
.
data
.
read
=
false
}
else
{
model
.
form
.
data
.
read
=
true
login
()
}
readConfirmShow
.
value
=
false
}
</
script
>
<
template
>
<view
class=
"login_warp"
>
<image
class=
"login_top_bg"
src=
"/static/images/login/login_top_bg.png"
/>
<view
class=
"login_top_warp"
>
<view
class=
"login_hello"
>
<text
class=
"text_hello"
>
您好,欢迎使用
</text>
<view
class=
"login_server_name"
><text
class=
"text_server_name"
>
湘农数智服务平台
</text></view>
<view
class=
"login-page"
>
<!-- 顶部设计:数智化农场全景感 -->
<view
class=
"header-visual"
>
<image
class=
"header-scene"
src=
"/static/images/login/farm_base_bg.png"
mode=
"aspectFill"
/>
<view
class=
"header-overlay"
></view>
<!-- 顶部装饰元素:漂浮的气象/农业设备(体现数智感) -->
<image
class=
"float-icon device-1"
src=
"/static/images/nongchang/device1.png"
mode=
"aspectFit"
/>
<image
class=
"float-icon device-2"
src=
"/static/images/nongchang/device2.png"
mode=
"aspectFit"
/>
<!-- 顶部状态栏占位与操作 -->
<view
class=
"top-nav-bar"
>
<view
class=
"close-btn"
@
click=
"goHome"
>
<fui-icon
name=
"close"
:size=
"48"
color=
"#fff"
/>
</view>
<view
class=
"switch-action"
@
click=
"switchMode(loginMode === 1 ? 2 : 1)"
>
<text>
{{
loginMode
===
1
?
'密码登录'
:
'验证码登录'
}}
</text>
<fui-icon
name=
"arrowright"
:size=
"24"
color=
"#fff"
/>
</view>
</view>
</view>
<fui-form
class=
"form"
ref=
"form"
top=
"0"
:padding=
"['0rpx', '0rpx']"
background=
"#e46962"
>
<view
class=
"login_content"
>
<view
class=
"login-input-area"
>
<view
class=
"user_phone"
>
<image
class=
"user_phone_img"
src=
"/static/images/register/user.png"
/>
<view
class=
"user_text_view"
><text
class=
"view_text"
>
账号
</text></view>
</view>
<view
class=
"input-bottom-border"
>
<fui-input
height=
"94rpx"
:padding=
"['0rpx', '0rpx', '0rpx', '12rpx']"
class=
"input"
autocomplete=
"off"
:required=
"false"
clearable
trim
placeholder=
"请输入账号"
v-model=
"model.form.data.username"
name=
"mobile"
backgroundColor=
"transparent"
borderColor=
"transparent"
maxlength=
"11"
/>
</view>
<view
class=
"user_phone mt50"
>
<image
class=
"user_phone_img"
src=
"/static/images/register/pwd.png"
/>
<view
class=
"user_text_view"
><text
class=
"view_text"
>
密码
</text></view>
</view>
<view
class=
"input-bottom-border"
>
<fui-input
height=
"94rpx"
:padding=
"['0rpx', '0rpx', '0rpx', '12rpx']"
class=
"input"
password
clearable
trim
placeholder=
"请输入密码"
v-model=
"model.form.data.password"
backgroundColor=
"transparent"
borderColor=
"transparent"
/>
</view>
<!-- 品牌标识与数智概览 -->
<view
class=
"brand-hero"
>
<view
class=
"logo-box"
>
<image
class=
"logo-img"
src=
"/static/logo.png"
mode=
"aspectFit"
/>
</view>
<!--
<view
class=
"login-input-area"
>
<view
class=
"user_phone"
>
<image
class=
"user_phone_img"
src=
"/static/images/register/user.png"
/>
<view
class=
"user_text_view"
><text
class=
"view_text"
>
手机号
</text></view>
<view
class=
"brand-info"
>
<text
class=
"app-title"
>
湘农数智农服
</text>
<view
class=
"app-slogan"
>
<image
src=
"/static/images/weather/100.svg"
class=
"weather-icon"
/>
<text>
智慧生产 · 农务管家
</text>
</view>
<view
class=
"input-bottom-border"
>
<fui-input
height=
"94rpx"
:padding=
"['0rpx', '0rpx', '0rpx', '12rpx']"
class=
"input"
autocomplete=
"off"
:required=
"false"
clearable
trim
type=
"number"
placeholder=
"请输入手机号"
v-model=
"model.form.data.username"
name=
"mobile"
backgroundColor=
"transparent"
borderColor=
"transparent"
maxlength=
"11"
/>
</view>
<view
class=
"user_phone mt50"
>
<image
class=
"user_phone_img"
src=
"/static/images/register/sms.png"
/>
<view
class=
"user_text_view"
><text
class=
"view_text"
>
验证码
</text></view>
</view>
<view
class=
"input-bottom-border"
>
<fui-input
height=
"94rpx"
:padding=
"['0rpx', '0rpx', '0rpx', '12rpx']"
class=
"input"
type=
"number"
placeholder=
"请输入验证码"
v-model=
"model.form.data.code"
backgroundColor=
"transparent"
borderColor=
"transparent"
><fui-button
width=
"200rpx"
height=
"64rpx"
:background=
"model.countdown > 0 ? '#CCCCCC' : '#67c17a'"
:color=
"model.countdown > 0 ? '#67c17a' : '#fff'"
@
click=
"smsCode"
:size=
"28"
:disabled=
"model.countdown > 0"
:text=
"model.countdown > 0 ? `$
{model.countdown}秒后重试` : '获取验证码'"
/>
</fui-input>
</view>
</view>
-->
<view
class=
"submit_btn_view"
>
<fui-button
height=
"72rpx"
background=
"#5DB66F"
size=
"28rpx"
radius=
"36rpx"
text=
"立即登录"
@
click=
"login"
:disabled=
"model.loading"
:loading=
"model.loading"
/>
</view>
</view>
</fui-form>
<!--
</view>
-->
<fui-checkbox-group
class=
"checkbox"
name=
"checkbox"
>
<view
class=
"fui-list__item fiexdText"
>
<view
class=
"fui-align__center"
style=
"justify-content: center"
>
<view
class=
"fui-text privacy-wrap"
style=
"font-size: 28rpx; text-align: center; align-items: center"
>
<fui-label
style=
"display: inline-flex; align-items: center"
>
<fui-checkbox
value=
"true"
color=
"#4da25b"
:checked=
"model.form.data.read"
@
change=
"(e) => (model.form.data.read = e.checked)"
style=
"margin-right: 10rpx; width: 32rpx; height: 32rpx; margin-top: 2rpx"
</view>
<!-- 登录卡片:磨砂玻璃质感 -->
<view
class=
"main-card-container"
>
<view
class=
"aesthetic-card"
>
<fui-form
ref=
"form"
top=
"0"
:padding=
"['0', '0']"
background=
"transparent"
>
<view
class=
"card-header"
>
<text
class=
"welcome-text"
>
您好,欢迎登录
</text>
<view
class=
"green-line"
></view>
</view>
<!-- 输入区域 -->
<view
class=
"input-fields"
>
<view
class=
"field-item"
>
<text
class=
"field-label"
>
{{
loginMode
===
1
?
'手机号'
:
'账号'
}}
</text>
<fui-input
v-model=
"model.form.data.username"
:padding=
"['24rpx', '0']"
:placeholder=
"loginMode === 1 ? '请输入手机号' : '请输入账号'"
backgroundColor=
"transparent"
borderColor=
"#F0F0F0"
borderBottom
height=
"110rpx"
size=
"34"
/>
<text
style=
"color: #999; font-size: 28rpx"
>
已阅读并同意
</text>
</fui-label>
<fui-text
@
tap=
"Link.to(Link.services, '服务协议')"
size=
"28rpx"
text=
"《服务协议》"
color=
"#4da25b"
/><text
style=
"color: #999; font-size: 28rpx"
>
与
</text>
<fui-text
@
tap=
"Link.to(Link.privacy, '隐私政策')"
size=
"28rpx"
text=
"《隐私政策》"
color=
"#4da25b"
/>
</view>
<view
class=
"field-item"
v-if=
"loginMode === 1"
>
<text
class=
"field-label"
>
验证码
</text>
<view
class=
"code-inner"
>
<fui-input
v-model=
"model.form.data.code"
:padding=
"['24rpx', '0']"
placeholder=
"请输入验证码"
backgroundColor=
"transparent"
borderColor=
"#F0F0F0"
borderBottom
height=
"110rpx"
size=
"34"
autocomplete=
"off"
/>
<view
class=
"sms-trigger"
:class=
"
{ inactive: model.countdown > 0 }" @click="smsCode">
{{
model
.
countdown
>
0
?
`${model.countdown
}
s后重发`
:
'获取验证码'
}}
<
/view
>
<
/view
>
<
/view
>
<
view
class
=
"field-item"
v
-
if
=
"loginMode === 2"
>
<
text
class
=
"field-label"
>
密码
<
/text
>
<
fui
-
input
v
-
model
=
"model.form.data.password"
type
=
"password"
:
padding
=
"['24rpx', '0']"
placeholder
=
"请输入密码"
backgroundColor
=
"transparent"
borderColor
=
"#F0F0F0"
borderBottom
height
=
"110rpx"
size
=
"34"
/>
<
/view
>
<
/view
>
</view>
<!-- 安全区 -->
<fui-safe-area
/>
</view>
</fui-checkbox-group>
<view
class=
"confirm-dialog-overlay"
v-show=
"readConfirmShow"
>
<view
class=
"confirm-dialog-container"
>
<!-- 标题 -->
<view
class=
"dialog-title"
>
服务协议及隐私保护
</view>
<!-- 内容 -->
<view
class=
"dialog-content"
>
<text
class=
"fui-descr"
>
<text>
为了更好地保障您的合法权益,请您阅读并同意以下协议
</text>
<fui-text
@
tap=
"Link.to(Link.services, '服务协议')"
size=
"28rpx"
text=
"《服务协议》"
color=
"#4da25b"
/>
和
<fui-text
@
tap=
"Link.to(Link.privacy, '隐私政策')"
size=
"28rpx"
text=
"《隐私政策》"
color=
"#4da25b"
<!--
登录决策
-->
<
view
class
=
"action-footer"
>
<
fui
-
button
text
=
"开启农务数字化"
background
=
"linear-gradient(90deg, #5DB66F 0%, #4CAF50 100%)"
radius
=
"100rpx"
height
=
"110rpx"
@
click
=
"login"
:
loading
=
"model.loading"
:
disabled
=
"model.loading"
size
=
"36"
shadow
/>
</text>
</view>
<!-- 按钮组 -->
<view
class=
"dialog-buttons"
>
<view
class=
"cancel-btn"
@
click=
"handleCancel"
>
<text
class=
"cancel-text"
>
不同意
</text>
<
view
class
=
"secondary-links"
>
<
view
class
=
"reg-btn"
@
click
=
"goRegister"
>
新用户注册
<
/view
>
<
/view
>
<
/view
>
<view
class=
"confirm-btn"
@
click=
"handleConfirm"
>
<text
class=
"confirm-text"
>
同意
</text>
<!--
隐私合规
-->
<
view
class
=
"policy-wrapper"
>
<
fui
-
checkbox
-
group
>
<
fui
-
label
class
=
"policy-label"
>
<
fui
-
checkbox
color
=
"#5DB66F"
:
scale
=
"0.7"
:
checked
=
"model.form.data.read"
@
change
=
"(e) => (model.form.data.read = e.checked)"
/>
<
view
class
=
"policy-text"
>
同意
<
text
class
=
"link"
@
click
.
stop
=
"Link.to(Link.services, '服务协议')"
>
《服务协议》
<
/text>与<text class="link" @click.stop="Link.to
(
Link.privacy, '隐私政策'
)
">《隐私政策》</
text
>
<
/view
>
<
/fui-label
>
<
/fui-checkbox-group
>
<
/view
>
</
view
>
<
/
fui-form
>
<
/view
>
<
/view
>
<!--
装饰底部:泥土与天空的呼应
-->
<
view
class
=
"footer-aesthetic"
><
/view
>
<!--
功能弹窗
-->
<
fui
-
modal
:
show
=
"readConfirmShow"
title
=
"服务协议及隐私保护"
:
buttons
=
"[{ text: '不同意', plain: true
}
, { text: '同意', plain: false, color: '#fff'
}
]"
@
click
=
"onReadConfirm"
>
<
view
class
=
"modal-notice"
>
点击同意即表示您已阅读并理解
<
text
class
=
"link-inline"
@
click
.
stop
=
"Link.to(Link.services, '服务协议')"
>
《服务协议》
<
/text
>
与
<
text
class
=
"link-inline"
@
click
.
stop
=
"Link.to(Link.privacy, '隐私政策')"
>
《隐私政策》
<
/text
>
<
/view
>
<
/fui-modal
>
<
SlideVerify
v
-
model
:
show
=
"showSlide"
@
success
=
"onSlideSuccess"
/>
<
/view
>
<
/template
>
<
style
lang
=
"less"
scoped
>
@keyframes
blink-caret
{
0
%,
100
%
{
border-right-color
:
#333
;
.
login
-
page
{
min
-
height
:
100
vh
;
background
-
color
:
#
f7faf8
;
display
:
flex
;
flex
-
direction
:
column
;
overflow
-
x
:
hidden
;
}
/* 顶部视觉层:农业生产全景 */
.
header
-
visual
{
height
:
600
rpx
;
position
:
relative
;
padding
-
top
:
var
(
--
status
-
bar
-
height
);
overflow
:
hidden
;
.
header
-
scene
{
position
:
absolute
;
width
:
100
%
;
height
:
100
%
;
top
:
0
;
left
:
0
;
filter
:
brightness
(
0.8
);
}
50
%
{
border-right-color
:
transparent
;
.
header
-
overlay
{
position
:
absolute
;
width
:
100
%
;
height
:
100
%
;
top
:
0
;
left
:
0
;
background
:
linear
-
gradient
(
180
deg
,
rgba
(
93
,
182
,
111
,
0.4
)
0
%
,
rgba
(
76
,
175
,
80
,
0.8
)
100
%
);
}
}
.login_warp
{
position
:
relative
;
font-size
:
24
rpx
;
height
:
calc
(
100vh
);
block-size
:
100%
100%
;
background-color
:
#fafefc
;
.login_top_bg
{
width
:
750
rpx
;
height
:
1324
rpx
;
/* 漂浮装饰:增强数智感 */
.
float
-
icon
{
position
:
absolute
;
left
:
0
rpx
;
top
:
0
rpx
;
opacity
:
0.6
;
filter
:
drop
-
shadow
(
0
4
rpx
10
rpx
rgba
(
0
,
0
,
0
,
0.2
));
&
.
device
-
1
{
width
:
120
rpx
;
height
:
120
rpx
;
right
:
-
20
rpx
;
top
:
150
rpx
;
transform
:
rotate
(
15
deg
);
animation
:
float
-
slow
4
s
ease
-
in
-
out
infinite
;
}
&
.
device
-
2
{
width
:
100
rpx
;
height
:
100
rpx
;
left
:
40
rpx
;
bottom
:
120
rpx
;
transform
:
rotate
(
-
10
deg
);
animation
:
float
-
slow
3
s
ease
-
in
-
out
infinite
alternate
;
}
}
.login_top_warp
{
width
:
750
rpx
;
height
:
482
rpx
;
.
top
-
nav
-
bar
{
position
:
relative
;
z
-
index
:
10
;
display
:
flex
;
justify
-
content
:
space
-
between
;
align
-
items
:
center
;
padding
:
20
rpx
40
rpx
;
.
switch
-
action
{
color
:
#
fff
;
font
-
size
:
28
rpx
;
display
:
flex
;
align
-
items
:
center
;
background
:
rgba
(
255
,
255
,
255
,
0.2
);
padding
:
10
rpx
24
rpx
;
border
-
radius
:
40
rpx
;
backdrop
-
filter
:
blur
(
4
px
);
}
}
.login_hello
{
position
:
absolute
;
left
:
50
rpx
;
top
:
226
rpx
;
color
:
rgb
(
51
51
51
/
100%
);
.text_hello
{
font-size
:
32
rpx
;
font-weight
:
400
;
letter-spacing
:
0
rpx
;
line-height
:
40
rpx
;
.
brand
-
hero
{
position
:
relative
;
z
-
index
:
10
;
display
:
flex
;
flex
-
direction
:
column
;
align
-
items
:
center
;
margin
-
top
:
50
rpx
;
.
logo
-
box
{
width
:
160
rpx
;
height
:
160
rpx
;
background
:
#
fff
;
border
-
radius
:
44
rpx
;
display
:
flex
;
align
-
items
:
center
;
justify
-
content
:
center
;
box
-
shadow
:
0
16
rpx
32
rpx
rgba
(
0
,
0
,
0
,
0.15
);
.
logo
-
img
{
width
:
100
rpx
;
height
:
100
rpx
;
}
}
.login_server_name
{
margin-top
:
32
rpx
;
.
brand
-
info
{
margin
-
top
:
30
rpx
;
text
-
align
:
center
;
color
:
#
fff
;
.text_server_name
{
font-size
:
40
rpx
;
font-weight
:
500
;
letter-spacing
:
0
rpx
;
line-height
:
40
rpx
;
}
.
app
-
title
{
font
-
size
:
48
rpx
;
font
-
weight
:
800
;
letter
-
spacing
:
4
rpx
;
text
-
shadow
:
0
4
rpx
8
rpx
rgba
(
0
,
0
,
0
,
0.2
);
}
}
}
.login_content
{
display
:
flex
;
justify-content
:
center
;
flex-wrap
:
wrap
;
position
:
relative
;
.login-input-area
{
width
:
650
rpx
;
//
border
:
1
rpx
red
solid
;
.user_phone
{
.
app
-
slogan
{
margin
-
top
:
12
rpx
;
font
-
size
:
26
rpx
;
opacity
:
0.9
;
display
:
flex
;
align
-
items
:
center
;
.user_phone_img
{
justify
-
content
:
center
;
.
weather
-
icon
{
width
:
40
rpx
;
height
:
40
rpx
;
margin
-
right
:
12
rpx
;
}
}
.user_text_view
{
margin-left
:
12
rpx
;
height
:
40
rpx
;
line-height
:
40
rpx
;
.view_text
{
font-size
:
30
rpx
;
font-weight
:
500
;
letter-spacing
:
0
rpx
;
color
:
rgb
(
51
51
51
/
100%
);
}
}
.input-bottom-border
{
border-bottom
:
2
rpx
#eee
solid
;
}
.mt50
{
margin-top
:
50
rpx
;
}
}
}
}
.submit_btn_view
{
margin-top
:
120
rpx
;
width
:
650
rpx
;
}
.fui-descr
{
letter-spacing
:
1
rpx
;
padding
:
50
rpx
;
font-size
:
24
rpx
;
color
:
#b2b2b2
;
padding-top
:
12
rpx
;
padding-bottom
:
48
rpx
;
/* 登录卡片层 */
.
main
-
card
-
container
{
padding
:
0
40
rpx
;
margin
-
top
:
-
100
rpx
;
position
:
relative
;
z
-
index
:
20
;
}
::v-deep(.fui-text__content)
{
text-indent
:
0
!important
;
.
aesthetic
-
card
{
background
:
#
ffffff
;
border
-
radius
:
56
rpx
;
padding
:
80
rpx
50
rpx
60
rpx
;
box
-
shadow
:
0
20
rpx
60
rpx
rgba
(
0
,
0
,
0
,
0.08
);
.
card
-
header
{
margin
-
bottom
:
60
rpx
;
.
welcome
-
text
{
font
-
size
:
40
rpx
;
font
-
weight
:
bold
;
color
:
#
333
;
}
.
green
-
line
{
width
:
60
rpx
;
height
:
8
rpx
;
background
:
#
5
DB66F
;
border
-
radius
:
4
rpx
;
margin
-
top
:
16
rpx
;
}
}
.form
{
position
:
absolute
;
top
:
480
rpx
;
z-index
:
10
;
}
.
field
-
item
{
margin
-
bottom
:
44
rpx
;
.checkbox
{
position
:
fixed
;
left
:
0
rpx
;
bottom
:
32
rpx
;
width
:
100%
;
z-index
:
10
;
}
.
field
-
label
{
font
-
size
:
26
rpx
;
color
:
#
999
;
margin
-
left
:
4
rpx
;
}
.privacy-wrap
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
.
code
-
inner
{
display
:
flex
;
align
-
items
:
center
;
justify
-
content
:
space
-
between
;
.
sms
-
trigger
{
white
-
space
:
nowrap
;
font
-
size
:
28
rpx
;
color
:
#
5
DB66F
;
padding
:
16
rpx
32
rpx
;
background
:
#
f1f9f2
;
border
-
radius
:
50
rpx
;
margin
-
left
:
20
rpx
;
font
-
weight
:
500
;
&
.
inactive
{
color
:
#
bbb
;
background
:
#
f8f8f8
;
}
}
}
}
}
.input
,
.btn__box
{
width
:
100%
;
height
:
100
rpx
;
margin-top
:
60
rpx
;
}
.
action
-
footer
{
margin
-
top
:
80
rpx
;
.fiexdText
{
width
:
100%
;
.
secondary
-
links
{
margin
-
top
:
40
rpx
;
}
text
-
align
:
center
;
:deep
(
.fui-input__border-bottom
)
{
right
:
32
rpx
!important
;
}
.
auto
-
hint
{
font
-
size
:
24
rpx
;
color
:
#
ccc
;
margin
-
bottom
:
24
rpx
;
}
.btn-register
{
color
:
cadetblue
;
.
reg
-
btn
{
font
-
size
:
30
rpx
;
color
:
#
666
;
font
-
weight
:
500
;
display
:
inline
-
block
;
padding
:
10
rpx
40
rpx
;
border
:
1
rpx
solid
#
eee
;
border
-
radius
:
40
rpx
;
}
}
}
.confirm-dialog-overlay
{
position
:
fixed
;
inset
:
0
;
background-color
:
rgb
(
0
0
0
/
50%
);
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
z-index
:
99999
;
}
.confirm-dialog-container
{
position
:
relative
;
width
:
600
rpx
;
background
:
linear-gradient
(
180deg
,
#e8f5e9
0%
,
#fff
30%
);
border-radius
:
32
rpx
;
padding
:
60
rpx
48
rpx
48
rpx
;
box-shadow
:
0
8
rpx
32
rpx
rgb
(
0
0
0
/
10%
);
}
.close-btn
{
position
:
absolute
;
top
:
24
rpx
;
right
:
24
rpx
;
width
:
48
rpx
;
height
:
48
rpx
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
}
.
policy
-
wrapper
{
margin
-
top
:
80
rpx
;
.
policy
-
label
{
display
:
flex
;
align
-
items
:
flex
-
start
;
}
.close-icon
{
font-size
:
36
rpx
;
color
:
#999
;
font-weight
:
300
;
}
.
policy
-
text
{
font
-
size
:
24
rpx
;
color
:
#
bbb
;
margin
-
left
:
12
rpx
;
line
-
height
:
1.4
;
}
.dialog-title
{
font-size
:
36
rpx
;
font-weight
:
600
;
color
:
#333
;
text-align
:
center
;
margin-bottom
:
32
rpx
;
.
link
{
color
:
#
5
DB66F
;
font
-
weight
:
500
;
}
}
.
dialog-content
{
.
modal
-
notice
{
font
-
size
:
28
rpx
;
color
:
#
666
;
text-align
:
center
;
line-height
:
40
rpx
;
margin-bottom
:
48
rpx
;
}
.dialog-buttons
{
display
:
flex
;
gap
:
24
rpx
;
line
-
height
:
1.8
;
.
link
-
inline
{
color
:
#
5
DB66F
;
font
-
weight
:
bold
;
}
}
.cancel-btn
,
.confirm-btn
{
.
footer
-
aesthetic
{
flex
:
1
;
height
:
80
rpx
;
border-radius
:
40
rpx
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
}
.cancel-btn
{
background-color
:
#fff
;
border
:
2
rpx
solid
#5db66f
;
}
.cancel-text
{
font-size
:
28
rpx
;
color
:
#5db66f
;
font-weight
:
500
;
}
.confirm-btn
{
background
:
linear-gradient
(
135deg
,
#5db66f
0%
,
#4caf50
100%
);
box-shadow
:
0
4
rpx
12
rpx
rgb
(
93
182
111
/
30%
);
}
.confirm-text
{
font-size
:
28
rpx
;
color
:
#fff
;
font-weight
:
500
;
}
.cancel-btn
:active
{
opacity
:
0.8
;
background
:
linear
-
gradient
(
180
deg
,
#
f7faf8
0
%
,
#
edf3ef
100
%
);
}
.confirm-btn
:active
{
opacity
:
0.9
;
transform
:
scale
(
0.98
);
@
keyframes
float
-
slow
{
0
%
,
100
%
{
transform
:
translateY
(
0
)
rotate
(
15
deg
);
}
50
%
{
transform
:
translateY
(
-
20
rpx
)
rotate
(
10
deg
);
}
}
<
/style
>
src/pages/login/register.vue
浏览文件 @
4a6f63f4
<
script
setup
lang=
"ts"
>
// import { PUSH_CLIENT_KEY } from '/@/enums/cacheEnum'
import
*
as
API
from
'/@/api/model/userInfo'
import
SlideVerify
from
'@/components/slide-verify/SlideVerify.vue'
onShow
(
async
()
=>
{})
// 页面数据
const
defaultText
=
'湖南省农业服务平台'
const
defaultText
=
'湘农数智农服'
const
form
=
ref
()
const
showSlide
=
ref
(
false
)
const
slideType
=
ref
<
'sms'
|
'register'
>
(
'register'
)
const
model
=
reactive
({
show
:
false
,
isLogin
:
false
,
loading
:
false
,
text
:
defaultText
,
countdown
:
0
,
// 倒计时秒数
countdownTimer
:
null
,
// 倒计时定时器
countdown
:
0
,
form
:
{
rules
:
[
{
name
:
'phone'
,
rule
:
[
'required'
],
msg
:
[
'请输入手机号'
],
rule
:
[
'required'
,
'isMobile'
],
msg
:
[
'请输入手机号'
,
'请输入正确的手机号'
],
},
{
name
:
'code'
,
rule
:
[
'required'
],
msg
:
[
'请输入验证码'
],
},
{
name
:
'password'
,
rule
:
[
'required'
,
'minLength:6'
],
msg
:
[
'请输入密码'
,
'密码长度不能少于6位'
],
},
],
rulesPhone
:
[
{
name
:
'phone'
,
rule
:
[
'required'
],
msg
:
[
'请输入手机号'
],
rule
:
[
'required'
,
'isMobile'
],
msg
:
[
'请输入手机号'
,
'请输入正确的手机号'
],
},
],
data
:
{
phone
:
''
,
password
:
'
123@2025
'
,
password
:
''
,
code
:
''
,
read
:
false
,
},
},
})
/**
* 注册
*/
let
countdownTimer
:
any
=
null
function
register
()
{
if
(
!
model
.
form
.
data
.
read
)
{
Message
.
toast
(
'请阅读并同意服务协议及隐私政策'
)
return
}
form
?.
value
.
validator
(
model
.
form
.
data
,
model
.
form
.
rules
).
then
(
async
(
res
:
{
isPassed
:
boolean
})
=>
{
if
(
res
.
isPassed
)
{
// 注册参数
const
params
=
{
phone
:
model
.
form
.
data
.
phone
,
password
:
model
.
form
.
data
.
password
,
code
:
model
.
form
.
data
.
code
,
}
// 短信登录
model
.
loading
=
true
API
.
sysRegister
(
params
)
.
then
(
async
(
body
)
=>
{
console
.
log
(
'body'
,
body
)
if
(
body
)
{
// 打开登录页
goLogin
()
Message
.
toast
(
`注册成功, 请登录~`
)
}
else
{
Message
.
toast
(
body
.
message
)
return
false
}
})
.
finally
(()
=>
{
model
.
loading
=
false
})
slideType
.
value
=
'register'
showSlide
.
value
=
true
}
})
}
/**
* 获取验证码
*/
function
doRegister
()
{
const
params
=
{
phone
:
model
.
form
.
data
.
phone
,
password
:
model
.
form
.
data
.
password
,
code
:
model
.
form
.
data
.
code
,
}
model
.
loading
=
true
API
.
sysRegister
(
params
)
.
then
(
async
(
body
)
=>
{
if
(
body
)
{
Message
.
toast
(
`注册成功, 请登录~`
)
setTimeout
(()
=>
{
goLogin
()
},
1500
)
}
})
.
finally
(()
=>
{
model
.
loading
=
false
})
}
function
smsCode
()
{
form
?.
value
.
validator
(
model
.
form
.
data
,
model
.
form
.
rulesPhone
).
then
(
async
(
res
:
{
isPassed
:
boolean
})
=>
{
if
(
res
.
isPassed
)
{
// 如果已经在倒计时中,不重复发送
if
(
model
.
countdown
>
0
)
{
return
}
const
params
=
{
mobile
:
model
.
form
.
data
.
phone
,
smsmode
:
1
,
}
API
.
sysSms
(
params
)
.
then
(
async
(
body
)
=>
{
Message
.
toast
(
'验证码已发送'
)
console
.
log
(
'body'
,
body
)
// 开始倒计时
startCountdown
()
})
.
catch
(()
=>
{
// 即使请求失败也显示倒计时,防止重复点击
startCountdown
()
})
if
(
model
.
countdown
>
0
)
return
slideType
.
value
=
'sms'
showSlide
.
value
=
true
}
})
}
/**
* 开始倒计时
*/
function
onSlideSuccess
()
{
if
(
slideType
.
value
===
'sms'
)
{
const
params
=
{
mobile
:
model
.
form
.
data
.
phone
,
smsmode
:
2
,
// 2-注册
}
API
.
sysSms
(
params
)
.
then
(
async
()
=>
{
Message
.
toast
(
'验证码已发送'
)
startCountdown
()
})
.
catch
(()
=>
{
Message
.
toast
(
'验证码发送失败'
)
})
}
else
{
doRegister
()
}
}
function
startCountdown
()
{
if
(
countdownTimer
)
clearInterval
(
countdownTimer
)
model
.
countdown
=
60
model
.
countdownTimer
=
setInterval
(()
=>
{
countdownTimer
=
setInterval
(()
=>
{
model
.
countdown
--
if
(
model
.
countdown
<=
0
)
{
clearInterval
(
model
.
countdownTimer
)
model
.
countdownTimer
=
null
clearInterval
(
countdownTimer
)
countdownTimer
=
null
}
},
1000
)
}
/**
* 跳转到登录页
*/
function
goLogin
()
{
uni
.
reLaunch
({
uni
.
navigateTo
({
url
:
'/pages/login/login'
,
})
}
// 添加欢迎登录的文字打字动态效果
let
loop
=
null
let
direction
=
'right'
const
count
=
ref
(
defaultText
.
length
)
watch
(
()
=>
model
.
show
,
(
show
)
=>
{
if
(
show
)
{
loop
&&
clearInterval
(
loop
)
loop
=
setInterval
(()
=>
{
if
(
direction
===
'right'
)
{
count
.
value
++
if
(
count
.
value
>
defaultText
.
length
+
20
)
{
direction
=
'left'
count
.
value
=
defaultText
.
length
}
}
else
{
count
.
value
--
if
(
count
.
value
<
0
)
{
direction
=
'right'
}
}
if
(
count
.
value
>
defaultText
.
length
)
{
model
.
text
=
defaultText
}
else
if
(
count
.
value
<
0
)
{
model
.
text
=
''
}
else
{
model
.
text
=
defaultText
.
slice
(
0
,
count
.
value
)
}
},
200
)
}
},
)
function
goHome
()
{
uni
.
reLaunch
({
url
:
'/pages/shouye/shouye'
,
})
}
onHide
(()
=>
{
loop
&&
clearInterval
(
loop
)
loop
=
null
direction
=
'right'
count
.
value
=
0
model
.
show
=
false
if
(
countdownTimer
)
{
clearInterval
(
countdownTimer
)
countdownTimer
=
null
}
})
onUnload
(()
=>
{
if
(
countdownTimer
)
{
clearInterval
(
countdownTimer
)
countdownTimer
=
null
}
})
</
script
>
<
template
>
<view
class=
"warp"
>
<!--
<image
class=
"login-warp"
src=
"/static/login/login_bg.png"
/>
-->
<view
class=
"register-form"
>
<view
class=
"register-bg-wrap"
>
<!--
<image
class=
"register-bg"
src=
"/static/images/register/register.png"
/>
-->
<view
class=
"logo-content-wrap"
>
<view
class=
"logo-text-1"
>
你好,欢迎使用
</view>
<view
class=
"logo-text"
>
数字农业服务平台
</view>
<view
class=
"login-page"
>
<!-- 顶部视觉层:阳光农场与气象设备 -->
<view
class=
"header-visual"
>
<image
class=
"header-scene"
src=
"/static/images/login/farm_base_bg.png"
mode=
"aspectFill"
/>
<view
class=
"header-overlay"
></view>
<!-- 气象站与农业设备:体现数智感 -->
<image
class=
"float-icon device-1"
src=
"/static/images/nongchang/device1.png"
mode=
"aspectFit"
/>
<image
class=
"float-icon device-2"
src=
"/static/images/nongchang/device3.png"
mode=
"aspectFit"
/>
<!-- 顶部导航 -->
<view
class=
"top-nav-bar"
>
<view
class=
"back-btn"
@
click=
"goLogin"
>
<fui-icon
name=
"arrowleft"
:size=
"48"
color=
"#fff"
/>
</view>
</view>
<fui-form
class=
"form"
ref=
"form"
top=
"50"
:padding=
"['0rpx', '32rpx']"
background=
"#e46962"
>
<view
class=
"reigister-form-item"
>
<image
class=
"reigister-form-image"
src=
"/static/images/register/user.png"
/>
<text>
手机号
</text>
</view>
<fui-input
height=
"100rpx"
class=
"input"
autocomplete=
"off"
:required=
"false"
clearable
trim
placeholder=
"请输入手机号/账号"
v-model=
"model.form.data.phone"
name=
"mobile"
backgroundColor=
"transparent"
borderColor=
"#DDDDDD"
maxlength=
"11"
/>
<view
class=
"reigister-form-item !hidden"
>
<image
class=
"reigister-form-image"
src=
"/static/images/register/pwd.png"
/>
<text>
密码
</text>
</view>
<fui-input
height=
"100rpx"
class=
"input !hidden"
password
autocomplete=
"new-password"
code
:required=
"false"
clearable
trim
placeholder=
"请输入密码"
v-model=
"model.form.data.password"
name=
"code"
marginTop=
"10"
backgroundColor=
"transparent"
borderColor=
"#DDDDDD"
/>
<view
class=
"reigister-form-item"
>
<image
class=
"reigister-form-image"
src=
"/static/images/register/sms.png"
/>
<text>
验证码
</text>
<!-- 品牌标识 -->
<view
class=
"brand-hero"
>
<view
class=
"logo-box"
>
<image
class=
"logo-img"
src=
"/static/logo.png"
mode=
"aspectFit"
/>
</view>
<fui-input
:padding=
"['20rpx', '32rpx']"
placeholder=
"请输入验证码"
:bottomLeft=
"0"
marginTop=
"10"
v-model=
"model.form.data.code"
backgroundColor=
"transparent"
borderColor=
"#DDDDDD"
>
<fui-button
width=
"200rpx"
height=
"64rpx"
:background=
"model.countdown > 0 ? '#CCCCCC' : '#67c17a'"
:color=
"model.countdown > 0 ? '#67c17a' : '#fff'"
@
click=
"smsCode"
:size=
"28"
:disabled=
"model.countdown > 0"
:text=
"model.countdown > 0 ? `$
{model.countdown}秒后重试` : '获取验证码'"
/>
</fui-input>
<view
class=
"btn__box flex-center p-32rpx box-border"
>
<fui-button
height=
"88rpx"
background=
"#67c17a"
size=
"32rpx"
radius=
"8rpx"
text=
"立即注册"
@
click=
"register"
:disabled=
"model.loading"
:loading=
"model.loading"
/>
<view
class=
"brand-info"
>
<text
class=
"app-title"
>
加入数智农服
</text>
<view
class=
"app-slogan"
>
<image
src=
"/static/images/weather/100.svg"
class=
"weather-icon"
/>
<text>
开启您的智慧农业之旅
</text>
</view>
</view>
<view
class=
"flex-center p-32rpx box-border btn-register"
@
click=
"goLogin"
>
已有账号,立即登录
</view>
</fui-form>
</view>
</view>
<!-- 注册卡片 -->
<view
class=
"main-card-container"
>
<view
class=
"aesthetic-card"
>
<fui-form
ref=
"form"
top=
"0"
:padding=
"['0', '0']"
background=
"transparent"
>
<view
class=
"card-header"
>
<text
class=
"welcome-text"
>
创建新账号
</text>
<view
class=
"green-line"
></view>
</view>
<view
class=
"input-fields"
>
<view
class=
"field-item"
>
<text
class=
"field-label"
>
手机号
</text>
<fui-input
v-model=
"model.form.data.phone"
:padding=
"['24rpx', '0']"
placeholder=
"请输入手机号"
backgroundColor=
"transparent"
borderColor=
"#F0F0F0"
borderBottom
height=
"110rpx"
size=
"34"
maxlength=
"11"
/>
</view>
<view
class=
"field-item"
>
<text
class=
"field-label"
>
验证码
</text>
<view
class=
"code-inner"
>
<fui-input
v-model=
"model.form.data.code"
:padding=
"['24rpx', '0']"
placeholder=
"请输入验证码"
backgroundColor=
"transparent"
borderColor=
"#F0F0F0"
borderBottom
height=
"110rpx"
size=
"34"
autocomplete=
"off"
/>
<view
class=
"sms-trigger"
:class=
"
{ inactive: model.countdown > 0 }" @click="smsCode">
{{
model
.
countdown
>
0
?
`${model.countdown
}
s后重发`
:
'获取验证码'
}}
<
/view
>
<
/view
>
<
/view
>
<
view
class
=
"field-item"
>
<
text
class
=
"field-label"
>
设置密码
<
/text
>
<
fui
-
input
v
-
model
=
"model.form.data.password"
type
=
"password"
:
padding
=
"['24rpx', '0']"
placeholder
=
"请设置6位以上登录密码"
backgroundColor
=
"transparent"
borderColor
=
"#F0F0F0"
borderBottom
height
=
"110rpx"
size
=
"34"
autocomplete
=
"new-password"
/>
<
/view
>
<
/view
>
<
view
class
=
"action-footer"
>
<
fui
-
button
text
=
"立即注册"
background
=
"linear-gradient(90deg, #5DB66F 0%, #4CAF50 100%)"
radius
=
"100rpx"
height
=
"110rpx"
@
click
=
"register"
:
loading
=
"model.loading"
:
disabled
=
"model.loading"
size
=
"36"
shadow
/>
<
view
class
=
"secondary-links"
>
<
view
class
=
"reg-btn"
@
click
=
"goLogin"
>
已有账号?去登录
<
/view
>
<
/view
>
<
/view
>
<!--
隐私合规:对齐优化
-->
<
view
class
=
"policy-wrapper"
>
<
fui
-
checkbox
-
group
>
<
fui
-
label
class
=
"policy-label"
>
<
view
class
=
"checkbox-align"
>
<
fui
-
checkbox
color
=
"#5DB66F"
:
scale
=
"0.7"
:
checked
=
"model.form.data.read"
@
change
=
"(e) => (model.form.data.read = e.checked)"
/>
<
/view
>
<
view
class
=
"policy-text"
>
同意
<
text
class
=
"link"
@
click
.
stop
=
"Link.to(Link.services, '服务协议')"
>
《服务协议》
<
/text>与<text class="link" @click.stop="Link.to
(
Link.privacy, '隐私政策'
)
">《隐私政策》</
text
>
<
/view
>
<
/fui-label
>
<
/fui-checkbox-group
>
<
/view
>
<
/fui-form
>
<
/view
>
<
/view
>
<
view
class
=
"footer-aesthetic"
><
/view
>
<
SlideVerify
v
-
model
:
show
=
"showSlide"
@
success
=
"onSlideSuccess"
/>
<
/view
>
<
/template
>
<
style
lang
=
"less"
scoped
>
//
.login-warp
{
//
width
:
100%
;
//
height
:
calc
(
100vh
);
//
position
:
absolute
;
//
//
border
:
1px
solid
#000
;
//
z-index
:
0
;
//
}
@keyframes
blink-caret
{
0
%,
100
%
{
border-right-color
:
#333
;
}
50
%
{
border-right-color
:
transparent
;
}
.
login
-
page
{
min
-
height
:
100
vh
;
background
-
color
:
#
f7faf8
;
display
:
flex
;
flex
-
direction
:
column
;
overflow
-
x
:
hidden
;
}
.warp
{
.
header
-
visual
{
height
:
550
rpx
;
position
:
relative
;
font-size
:
24
rpx
;
height
:
calc
(
100vh
);
block-size
:
100%
100%
;
//
background-size
:
100%
100%
;
//
border
:
1px
solid
#000
;
background-color
:
#fff
;
}
padding
-
top
:
var
(
--
status
-
bar
-
height
);
overflow
:
hidden
;
.
header
-
scene
{
position
:
absolute
;
width
:
100
%
;
height
:
100
%
;
top
:
0
;
left
:
0
;
filter
:
brightness
(
0.9
);
}
.fui-descr
{
letter-spacing
:
1
rpx
;
padding
:
50
rpx
;
font-size
:
24
rpx
;
color
:
#b2b2b2
;
padding-top
:
12
rpx
;
padding-bottom
:
48
rpx
;
.
header
-
overlay
{
position
:
absolute
;
width
:
100
%
;
height
:
100
%
;
top
:
0
;
left
:
0
;
background
:
linear
-
gradient
(
180
deg
,
rgba
(
93
,
182
,
111
,
0.3
)
0
%
,
rgba
(
76
,
175
,
80
,
0.7
)
100
%
);
}
::v-deep(.fui-text__content)
{
text-indent
:
0
!important
;
.
float
-
icon
{
position
:
absolute
;
opacity
:
0.7
;
filter
:
drop
-
shadow
(
0
4
rpx
10
rpx
rgba
(
0
,
0
,
0
,
0.15
));
&
.
device
-
1
{
width
:
130
rpx
;
height
:
130
rpx
;
right
:
20
rpx
;
top
:
180
rpx
;
transform
:
rotate
(
10
deg
);
animation
:
float
-
slow
4
s
ease
-
in
-
out
infinite
;
}
&
.
device
-
2
{
width
:
110
rpx
;
height
:
110
rpx
;
left
:
30
rpx
;
bottom
:
100
rpx
;
animation
:
float
-
slow
3
s
ease
-
in
-
out
infinite
alternate
;
}
}
}
.register-bg-wrap
{
position
:
absolute
;
width
:
100%
;
height
:
30vh
;
//
border
:
1px
solid
#000
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
center
;
align-items
:
center
;
background-image
:
url('/static/images/register/register.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
background-position
:
center
;
.logo-content-wrap
{
.
top
-
nav
-
bar
{
position
:
relative
;
z
-
index
:
10
;
padding
:
20
rpx
40
rpx
;
}
.
brand
-
hero
{
position
:
relative
;
z
-
index
:
10
;
display
:
flex
;
flex
-
direction
:
column
;
justify-content
:
left
;
align-items
:
flex-start
;
width
:
86%
;
.logo-text-1
{
font-size
:
30
rpx
;
font-weight
:
400
;
letter-spacing
:
0
;
margin-top
:
4.25rem
;
color
:
rgb
(
51
51
51
/
70%
);
vertical-align
:
middle
;
align
-
items
:
center
;
margin
-
top
:
20
rpx
;
.
logo
-
box
{
width
:
140
rpx
;
height
:
140
rpx
;
background
:
#
fff
;
border
-
radius
:
40
rpx
;
display
:
flex
;
align
-
items
:
center
;
justify
-
content
:
center
;
box
-
shadow
:
0
12
rpx
24
rpx
rgba
(
0
,
0
,
0
,
0.1
);
.
logo
-
img
{
width
:
90
rpx
;
height
:
90
rpx
;
}
}
.logo-text
{
font-size
:
40
rpx
;
font-weight
:
500
;
letter-spacing
:
0
;
margin-top
:
40
rpx
;
color
:
rgb
(
51
51
51
/
100%
);
vertical-align
:
middle
;
.
brand
-
info
{
margin
-
top
:
24
rpx
;
text
-
align
:
center
;
color
:
#
fff
;
.
app
-
title
{
font
-
size
:
44
rpx
;
font
-
weight
:
800
;
letter
-
spacing
:
2
rpx
;
text
-
shadow
:
0
2
rpx
4
rpx
rgba
(
0
,
0
,
0
,
0.1
);
}
.
app
-
slogan
{
margin
-
top
:
8
rpx
;
font
-
size
:
24
rpx
;
opacity
:
0.95
;
display
:
flex
;
align
-
items
:
center
;
justify
-
content
:
center
;
.
weather
-
icon
{
width
:
36
rpx
;
height
:
36
rpx
;
margin
-
right
:
10
rpx
;
}
}
}
}
}
.register-form
{
position
:
absolute
;
width
:
100%
;
height
:
60vh
;
left
:
0
;
top
:
0
;
opacity
:
1
;
border-radius
:
14.03px
14.03px
0
0
;
border
:
1px
solid
#fff
;
background
:
linear-gradient
(
180deg
,
rgb
(
181
238
215
/
100%
)
0%
,
rgb
(
181
238
215
/
50%
)
30%
,
rgb
(
255
255
255
/
80%
)
100%
);
.
main
-
card
-
container
{
padding
:
0
40
rpx
;
margin
-
top
:
-
80
rpx
;
position
:
relative
;
z
-
index
:
20
;
}
.reigister-form-item
{
color
:
#000
;
display
:
flex
;
flex-direction
:
row
;
justify-content
:
left
;
align-items
:
center
;
margin
:
10
rpx
;
.reigister-form-image
{
width
:
60
rpx
;
height
:
60
rpx
;
.
aesthetic
-
card
{
background
:
#
ffffff
;
border
-
radius
:
50
rpx
;
padding
:
70
rpx
50
rpx
60
rpx
;
box
-
shadow
:
0
15
rpx
50
rpx
rgba
(
0
,
0
,
0
,
0.06
);
.
card
-
header
{
margin
-
bottom
:
50
rpx
;
.
welcome
-
text
{
font
-
size
:
38
rpx
;
font
-
weight
:
bold
;
color
:
#
333
;
}
.
green
-
line
{
width
:
50
rpx
;
height
:
7
rpx
;
background
:
#
5
DB66F
;
border
-
radius
:
4
rpx
;
margin
-
top
:
14
rpx
;
}
}
text
{
font-size
:
32
rpx
;
display
:
flex
;
flex-direction
:
row
;
margin-left
:
20
rpx
;
.
field
-
item
{
margin
-
bottom
:
40
rpx
;
.
field
-
label
{
font
-
size
:
26
rpx
;
color
:
#
999
;
margin
-
left
:
4
rpx
;
}
.
code
-
inner
{
display
:
flex
;
align
-
items
:
center
;
justify
-
content
:
space
-
between
;
.
sms
-
trigger
{
white
-
space
:
nowrap
;
font
-
size
:
27
rpx
;
color
:
#
5
DB66F
;
padding
:
14
rpx
28
rpx
;
background
:
#
f1f9f2
;
border
-
radius
:
50
rpx
;
margin
-
left
:
20
rpx
;
font
-
weight
:
500
;
&
.
inactive
{
color
:
#
bbb
;
background
:
#
f8f8f8
;
}
}
}
}
}
.form
{
width
:
100%
;
z-index
:
10
;
margin-top
:
540
rpx
;
}
.
action
-
footer
{
margin
-
top
:
70
rpx
;
.checkbox
{
position
:
fixed
;
left
:
0
rpx
;
bottom
:
32
rpx
;
width
:
100%
;
z-index
:
10
;
}
.
secondary
-
links
{
margin
-
top
:
40
rpx
;
text
-
align
:
center
;
.privacy-wrap
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
.
reg
-
btn
{
font
-
size
:
28
rpx
;
color
:
#
888
;
padding
:
10
rpx
40
rpx
;
border
:
1
rpx
solid
#
f0f0f0
;
border
-
radius
:
40
rpx
;
}
}
}
.input
,
.btn__box
{
width
:
100%
;
height
:
100
rpx
;
.
policy
-
wrapper
{
margin
-
top
:
60
rpx
;
}
.
policy
-
label
{
display
:
flex
;
align
-
items
:
center
;
/* 垂直居中 */
justify
-
content
:
center
;
}
.fiexdText
{
//
position
:
absolute
;
width
:
100%
;
margin-top
:
40
rpx
;
.
checkbox
-
align
{
line
-
height
:
1
;
display
:
flex
;
align
-
items
:
center
;
}
.
policy
-
text
{
font
-
size
:
24
rpx
;
color
:
#
bbb
;
margin
-
left
:
12
rpx
;
display
:
flex
;
align
-
items
:
center
;
}
.
link
{
color
:
#
5
DB66F
;
font
-
weight
:
500
;
}
}
:deep
(
.fui-input__border-bottom
)
{
right
:
32
rpx
!important
;
.
footer
-
aesthetic
{
flex
:
1
;
background
:
linear
-
gradient
(
180
deg
,
#
f7faf8
0
%
,
#
edf3ef
100
%
);
}
.btn-register
{
color
:
cadetblue
;
@
keyframes
float
-
slow
{
0
%
,
100
%
{
transform
:
translateY
(
0
)
rotate
(
5
deg
);
}
50
%
{
transform
:
translateY
(
-
15
rpx
)
rotate
(
-
5
deg
);
}
}
<
/style
>
src/pages/mine/index.vue
浏览文件 @
4a6f63f4
...
...
@@ -315,7 +315,7 @@
}
.page-bg
{
background
:
url('/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
no-repeat
top
center
;
background
:
url('
../..
/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
no-repeat
top
center
;
background-size
:
100%
;
}
...
...
src/pages/nongchang/nongchang.vue
浏览文件 @
4a6f63f4
...
...
@@ -1470,7 +1470,7 @@
.image-wrapper
{
padding-bottom
:
104
rpx
;
background-image
:
url('/static/images/codefun/e18202eb8182b8d77c464523c2305fa3.png')
;
background-image
:
url('
../..
/static/images/codefun/e18202eb8182b8d77c464523c2305fa3.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
...
...
@@ -1912,7 +1912,7 @@
}
.nongchang_box
{
background-image
:
url('/static/images/nongchang/mynongchang-2.png')
;
background-image
:
url('
../..
/static/images/nongchang/mynongchang-2.png')
;
background-repeat
:
no-repeat
;
background-size
:
100%
100%
;
}
...
...
src/pages/nongjifuwu/components/apply-dialog.vue
浏览文件 @
4a6f63f4
...
...
@@ -83,36 +83,239 @@
</
script
>
<
template
>
<fui-dialog
title=
""
:buttons=
"[]"
:show=
"pageData.show"
maskClosable
@
close=
"close"
>
<view
class=
"dialog-header"
>
<text
class=
"dialog-title"
>
预约登记
</text>
<fui-icon
name=
"close"
:size=
"40"
color=
"#999"
@
click=
"close"
></fui-icon>
</view>
<view
class=
"dialog-body"
>
<fui-form
ref=
"formRef"
>
<fui-input
label=
"联系电话"
placeholder=
"请输入手机号"
v-model=
"pageData.form.phone"
required
type=
"number"
maxlength=
"11"
/>
<fui-input
label=
"作业区域"
placeholder=
"请选择"
v-model=
"pageData.scopeText"
@
click=
"pageData.areaShow.address = true"
disabled
required
/>
<fui-input
label=
"详细地址"
placeholder=
"村组/街道门牌"
v-model=
"pageData.form.address"
required
/>
<fui-input
label=
"作业时间"
placeholder=
"请选择周期"
v-model=
"pageData.form.time"
@
click=
"pageData.areaShow.time = true"
disabled
required
/>
<fui-textarea
label=
"需求说明"
placeholder=
"简要说明作业要求..."
v-model=
"pageData.form.demand"
required
height=
"120rpx"
/>
<view
class=
"submit-btn-wrap"
>
<fui-button
text=
"立即预约"
radius=
"100rpx"
background=
"#5db66f"
@
click=
"submit"
/>
<fui-bottom-popup
:show=
"pageData.show"
@
close=
"close"
>
<view
class=
"popup-wrap"
>
<!-- 弹窗头部 -->
<view
class=
"popup-header"
>
<view
class=
"header-left"
>
<view
class=
"header-icon"
>
<fui-icon
name=
"notice"
:size=
"32"
color=
"#fff"
></fui-icon>
</view>
<text
class=
"header-title"
>
预约登记
</text>
</view>
<view
class=
"close-btn"
@
click=
"close"
>
<fui-icon
name=
"close"
:size=
"36"
color=
"#999"
></fui-icon>
</view>
</view>
<!-- 表单区域 -->
<view
class=
"form-section"
>
<!-- 联系电话 -->
<view
class=
"form-item"
>
<view
class=
"form-label"
>
<text
class=
"label-text"
>
联系电话
</text>
<text
class=
"required"
>
*
</text>
</view>
<input
class=
"form-input"
type=
"number"
maxlength=
"11"
placeholder=
"请输入手机号"
v-model=
"pageData.form.phone"
placeholder-class=
"input-placeholder"
/>
</view>
<!-- 作业区域 -->
<view
class=
"form-item"
@
click=
"pageData.areaShow.address = true"
>
<view
class=
"form-label"
>
<text
class=
"label-text"
>
作业区域
</text>
<text
class=
"required"
>
*
</text>
</view>
<view
class=
"form-picker"
>
<text
:class=
"['picker-text', pageData.scopeText ? '' : 'placeholder']"
>
{{
pageData
.
scopeText
||
'请选择作业区域'
}}
</text>
<fui-icon
name=
"arrowright"
:size=
"28"
color=
"#ccc"
></fui-icon>
</view>
</view>
<!-- 详细地址 -->
<view
class=
"form-item"
>
<view
class=
"form-label"
>
<text
class=
"label-text"
>
详细地址
</text>
<text
class=
"required"
>
*
</text>
</view>
<input
class=
"form-input"
placeholder=
"村组/街道门牌号"
v-model=
"pageData.form.address"
placeholder-class=
"input-placeholder"
/>
</view>
<!-- 作业时间 -->
<view
class=
"form-item"
@
click=
"pageData.areaShow.time = true"
>
<view
class=
"form-label"
>
<text
class=
"label-text"
>
作业时间
</text>
<text
class=
"required"
>
*
</text>
</view>
<view
class=
"form-picker"
>
<text
:class=
"['picker-text', pageData.form.time ? '' : 'placeholder']"
>
{{
pageData
.
form
.
time
||
'请选择作业周期'
}}
</text>
<fui-icon
name=
"arrowright"
:size=
"28"
color=
"#ccc"
></fui-icon>
</view>
</view>
<!-- 需求说明 -->
<view
class=
"form-item"
>
<view
class=
"form-label"
>
<text
class=
"label-text"
>
需求说明
</text>
<text
class=
"required"
>
*
</text>
</view>
<textarea
class=
"form-textarea"
placeholder=
"请简要说明作业要求..."
v-model=
"pageData.form.demand"
placeholder-class=
"input-placeholder"
/>
</view>
</fui-form>
</view>
<!-- 提交按钮 -->
<view
class=
"submit-area"
>
<view
class=
"submit-btn"
@
click=
"submit"
>
<text
class=
"btn-text"
>
立即预约
</text>
</view>
</view>
</view>
</fui-
dialog
>
</fui-
bottom-popup
>
<fui-date-picker
:show=
"pageData.areaShow.time"
type=
"3"
range
@
change=
"handleTimeChange"
@
cancel=
"pageData.areaShow.time = false"
/>
<AreaPicker
v-model:show=
"pageData.areaShow.address"
:layer=
"3"
title=
"选择作业区域"
@
confirm=
"handleAreaConfirm"
/>
</
template
>
<
style
lang=
"scss"
scoped
>
.dialog-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
20
rpx
0
;
.dialog-title
{
font-size
:
32
rpx
;
font-weight
:
bold
;
color
:
#333
;
}
.popup-wrap
{
background-color
:
#fff
;
border-radius
:
32
rpx
32
rpx
0
0
;
overflow
:
hidden
;
padding-bottom
:
env
(
safe-area-inset-bottom
);
.popup-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
32
rpx
32
rpx
24
rpx
;
background
:
linear-gradient
(
135deg
,
#f8fdf9
0%
,
#fff
100%
);
border-bottom
:
1
rpx
solid
#f0f0f0
;
.header-left
{
display
:
flex
;
align-items
:
center
;
}
.header-icon
{
width
:
56
rpx
;
height
:
56
rpx
;
background
:
linear-gradient
(
135deg
,
#fa8c16
0%
,
#fa541c
100%
);
border-radius
:
14
rpx
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
margin-right
:
16
rpx
;
box-shadow
:
0
4
rpx
12
rpx
rgba
(
250
,
140
,
22
,
0.3
);
}
.header-title
{
font-size
:
32
rpx
;
font-weight
:
600
;
color
:
#333
;
}
.close-btn
{
width
:
56
rpx
;
height
:
56
rpx
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
background-color
:
#f5f5f5
;
border-radius
:
50%
;
}
}
.form-section
{
padding
:
24
rpx
32
rpx
0
;
.form-item
{
padding
:
20
rpx
0
;
border-bottom
:
1
rpx
solid
#f5f5f5
;
&:last-child
{
border-bottom
:
none
;
}
}
.form-label
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
14
rpx
;
.label-text
{
font-size
:
28
rpx
;
color
:
#333
;
font-weight
:
500
;
}
.required
{
color
:
#ff4d4f
;
margin-left
:
4
rpx
;
font-size
:
28
rpx
;
}
}
.form-input
{
width
:
100%
;
height
:
80
rpx
;
background-color
:
#f7f8fa
;
border-radius
:
12
rpx
;
padding
:
0
24
rpx
;
font-size
:
28
rpx
;
color
:
#333
;
box-sizing
:
border-box
;
}
.form-picker
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
height
:
80
rpx
;
background-color
:
#f7f8fa
;
border-radius
:
12
rpx
;
padding
:
0
24
rpx
;
.picker-text
{
font-size
:
28
rpx
;
color
:
#333
;
&.placeholder
{
color
:
#bbb
;
}
}
}
.form-textarea
{
width
:
100%
;
height
:
160
rpx
;
background-color
:
#f7f8fa
;
border-radius
:
12
rpx
;
padding
:
20
rpx
24
rpx
;
font-size
:
28
rpx
;
color
:
#333
;
box-sizing
:
border-box
;
line-height
:
1.5
;
}
}
.submit-area
{
padding
:
32
rpx
32
rpx
40
rpx
;
.submit-btn
{
width
:
100%
;
height
:
88
rpx
;
background
:
linear-gradient
(
135deg
,
#fa8c16
0%
,
#fa541c
100%
);
border-radius
:
44
rpx
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
box-shadow
:
0
8
rpx
24
rpx
rgba
(
250
,
140
,
22
,
0.4
);
.btn-text
{
font-size
:
30
rpx
;
color
:
#fff
;
font-weight
:
600
;
letter-spacing
:
4
rpx
;
}
}
}
}
.input-placeholder
{
color
:
#bbb
;
font-size
:
28
rpx
;
}
.dialog-body
{
text-align
:
left
;
padding-bottom
:
20
rpx
;
}
.submit-btn-wrap
{
margin-top
:
40
rpx
;
}
:deep
(
.fui-input__label
)
{
font-size
:
28
rpx
;
width
:
160
rpx
!important
;
}
</
style
>
src/pages/nongyedamoxing/nongyedamoxing.vue
浏览文件 @
4a6f63f4
...
...
@@ -254,7 +254,7 @@
overflow-x
:
hidden
;
.group
{
.section_2
{
background-image
:
url('/static/images/codefun/c122979639a1f9d27d7c57245ab420a6.png')
;
background-image
:
url('
../..
/static/images/codefun/c122979639a1f9d27d7c57245ab420a6.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.section_3
{
...
...
@@ -601,7 +601,7 @@
backdrop-filter
:
blur
(
4
rpx
);
.image-wrapper
{
padding
:
48
rpx
0
;
background-image
:
url('/static/images/codefun/5149f97303e2fa97daa823a1452dce11.png')
;
background-image
:
url('
../..
/static/images/codefun/5149f97303e2fa97daa823a1452dce11.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
width
:
240
rpx
;
...
...
src/pages/nongzhi/nongzhi.vue
浏览文件 @
4a6f63f4
...
...
@@ -506,7 +506,7 @@ e:\Downloads\农场 (1).png
.section
{
padding
:
24
rpx
28
rpx
;
background-image
:
url('/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-image
:
url('
../..
/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
.group
{
...
...
src/pages/shouye/shouye.vue
浏览文件 @
4a6f63f4
...
...
@@ -911,7 +911,7 @@ export default {
.section
{
padding
:
10
rpx
28
rpx
220
rpx
;
padding-top
:
calc
(
10
rpx
+
var
(
--status-bar-height
));
background-image
:
url('/static/images/codefun/1086a098c06f7f52e77bd7a646747a13.png')
;
background-image
:
url('
../..
/static/images/codefun/1086a098c06f7f52e77bd7a646747a13.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
...
...
src/pages/wode/wode.vue
浏览文件 @
4a6f63f4
...
...
@@ -451,7 +451,7 @@ showAddDialog()
.group
{
.section
{
padding
:
24
rpx
20
rpx
88
rpx
;
background-image
:
url('/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-image
:
url('
../..
/static/images/codefun/7a5dc4ee864fe55da98b41c14ee3b931.png')
;
background-size
:
100%
100%
;
background-repeat
:
no-repeat
;
...
...
src/store/modules/user.ts
浏览文件 @
4a6f63f4
...
...
@@ -82,6 +82,42 @@ export const useUserStore = defineStore({
Storage
.
set
(
USER_INFO_KEY
,
info
?
JSON
.
stringify
(
info
)
:
null
)
}
},
async
login
(
params
:
{
username
:
string
;
password
:
string
})
{
if
(
this
.
loading
)
{
return
}
this
.
loading
=
true
try
{
const
body
=
await
API
.
sysLogin
(
params
)
if
(
body
?.
token
)
{
this
.
setToken
(
body
.
token
)
}
const
userInfo
=
await
API
.
getUserInfo
()
this
.
setUserInfo
(
userInfo
)
return
body
}
finally
{
this
.
loading
=
false
}
},
async
phoneLogin
(
params
:
{
mobile
:
string
;
captcha
:
string
})
{
if
(
this
.
loading
)
{
return
}
this
.
loading
=
true
try
{
const
body
=
await
API
.
phoneLogin
(
params
)
if
(
body
?.
token
)
{
this
.
setToken
(
body
.
token
)
}
const
userInfo
=
await
API
.
getUserInfo
()
this
.
setUserInfo
(
userInfo
)
return
body
}
finally
{
this
.
loading
=
false
}
},
async
logout
()
{
if
(
this
.
loading
)
{
return
...
...
types/components.d.ts
浏览文件 @
4a6f63f4
...
...
@@ -153,6 +153,7 @@ declare module 'vue' {
Mapbox
:
typeof
import
(
'./../src/components/Map/Mapbox/index.vue'
)[
'default'
]
RouterLink
:
typeof
import
(
'vue-router'
)[
'RouterLink'
]
RouterView
:
typeof
import
(
'vue-router'
)[
'RouterView'
]
SlideVerify
:
typeof
import
(
'./../src/components/slide-verify/SlideVerify.vue'
)[
'default'
]
Src
:
typeof
import
(
'./../src/components/Echarts/src/index.vue'
)[
'default'
]
SuccessfulDialog
:
typeof
import
(
'./../src/components/ConfirmDialog/successfulDialog.vue'
)[
'default'
]
Switch
:
typeof
import
(
'./../src/components/Map/Widgets/Switch/src/Switch.vue'
)[
'default'
]
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论