新增定制需求功能和营销素材
This commit is contained in:
@@ -1,77 +1,277 @@
|
||||
<template name="basics">
|
||||
<view>
|
||||
<scroll-view scroll-y class="page">
|
||||
<view class="response"
|
||||
style="height: 250upx;line-height: 400upx;font-size: 32rpx;font-weight: bold;text-align: center;">
|
||||
敬请期待更多类别</view>
|
||||
<view class="nav-list">
|
||||
<navigator hover-class="none" :url="'/pages/tool/' + item.name" class="nav-li" navigateTo :class="'bg-'+item.color"
|
||||
:style="[{animation: 'show ' + ((index+1)*0.2+1) + 's 1'}]" v-for="(item,index) in elements" :key="index">
|
||||
<view class="mt30 grid text-center bg-white" style="flex-direction: column; align-items: center;">
|
||||
<view class=" flex justify-center align-center" style="width: 100upx;height: 100upx;">
|
||||
<image class="grid-svg" style="width: 100%;height: 100%;" :src="item.cuIcon" mode="">
|
||||
</image>
|
||||
</view>
|
||||
<view style="font-weight: bold;margin-top: 16rpx;">{{item.title}}</view>
|
||||
<view style="font-size: 24rpx;color: #b1b1b1;">{{item.name}}</view>
|
||||
<template>
|
||||
<view class="custom-page">
|
||||
<view class="custom-content">
|
||||
<view class="service-card">
|
||||
<view class="service-head">
|
||||
<view class="service-icon"><text class="cuIcon-writefill"></text></view>
|
||||
<view class="service-content">
|
||||
<view class="service-title">提交项目需求</view>
|
||||
<view class="service-desc">填写名称和核心需求,平台审核后进入开发流程</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</view>
|
||||
<view class="flow-row">
|
||||
<view class="flow-item"><text class="flow-number">1</text><text>提交需求</text></view>
|
||||
<text class="flow-line"></text>
|
||||
<view class="flow-item"><text class="flow-number">2</text><text>平台审核</text></view>
|
||||
<text class="flow-line"></text>
|
||||
<view class="flow-item"><text class="flow-number">3</text><text>查看进度</text></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cu-tabbar-height"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="form-card">
|
||||
<view class="form-title-row">
|
||||
<view class="form-title"><text class="cuIcon-titles title-icon"></text>填写需求</view>
|
||||
</view>
|
||||
|
||||
<view class="field-label">项目名称<text class="required">*</text></view>
|
||||
<view class="input-box">
|
||||
<input v-model="form.projectName" maxlength="30" placeholder="例如:校园二手交易小程序" placeholder-class="input-placeholder" />
|
||||
<text class="word-count">{{ projectNameLength }}/30</text>
|
||||
</view>
|
||||
|
||||
<view class="field-label field-label-space">技术栈<text class="required">*</text></view>
|
||||
<view v-if="techStacks.length" class="tech-stack-options">
|
||||
<view
|
||||
v-for="item in techStacks"
|
||||
:key="item.id"
|
||||
class="tech-stack-option"
|
||||
:class="{'tech-stack-selected': form.techStackId === item.id}"
|
||||
@tap="selectTechStack(item.id)"
|
||||
>{{ item.name }}</view>
|
||||
</view>
|
||||
<view v-else class="tech-stack-empty">{{ techStackLoading ? '正在加载技术栈...' : '暂无可选技术栈,请联系南音' }}</view>
|
||||
|
||||
<view class="field-label field-label-space">项目需求<text class="required">*</text></view>
|
||||
<view class="textarea-box">
|
||||
<textarea v-model="form.requirementContent" maxlength="500" placeholder="请写清楚谁来使用、主要解决什么问题,以及希望有哪些核心功能。" placeholder-class="input-placeholder" />
|
||||
<text class="word-count textarea-count">{{ requirementLength }}/500</text>
|
||||
</view>
|
||||
|
||||
<view class="price-card">
|
||||
<view class="price-header">
|
||||
<view class="price-title"><text class="cuIcon-moneybagfill price-title-icon"></text>定制费用说明</view>
|
||||
<view class="price-stage">先看演示再购买</view>
|
||||
</view>
|
||||
<view class="price-item">
|
||||
<view class="price-content">
|
||||
<view class="price-name">提交需求</view>
|
||||
<view class="price-desc">审核未通过自动退回,审核通过后不退</view>
|
||||
</view>
|
||||
<view class="price-value points-value">99<text>积分</text></view>
|
||||
</view>
|
||||
<view class="price-item">
|
||||
<view class="price-content">
|
||||
<view class="price-name">获取源码</view>
|
||||
<view class="price-desc">交付完整源码,含1次本地部署服务;项目可能继续在小程序内发布</view>
|
||||
</view>
|
||||
<view class="price-value"><text>¥</text>99</view>
|
||||
</view>
|
||||
<view class="price-item">
|
||||
<view class="price-content">
|
||||
<view class="price-name">独家买断</view>
|
||||
<view class="price-desc">交付完整源码,含1次本地部署服务和1次集中修改;项目不再发布到小程序</view>
|
||||
</view>
|
||||
<view class="price-value"><text>¥</text>299</view>
|
||||
</view>
|
||||
<view class="price-tip"><text class="cuIcon-infofill"></text>项目开发完成后先查看演示,满意后再选择源码交付方式</view>
|
||||
<view class="service-note">
|
||||
<view class="service-note-title">服务说明</view>
|
||||
<view>两种方案均包含交付后30天内原需求范围内的Bug修复。本地部署是指协助在1台电脑上完成运行环境配置、数据库导入及项目启动,不包含服务器上线。修改服务仅限原需求范围内调整,新增功能需另行评估。</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rule-box">
|
||||
<text class="cuIcon-info rule-icon"></text>
|
||||
<view>提交时直接扣除99积分;审核不通过将自动退回,审核通过后不再退还。</view>
|
||||
</view>
|
||||
|
||||
<button class="submit-button" :disabled="submitting" @tap="handleSubmit">
|
||||
{{ submitting ? '正在提交...' : '提交需求 · 99积分' }}
|
||||
</button>
|
||||
<view class="agreement">点击提交即表示你已了解积分扣除与退回规则</view>
|
||||
</view>
|
||||
|
||||
<view class="my-custom-entry" @tap="goMyCustom">
|
||||
<view class="entry-left">
|
||||
<text class="cuIcon-form entry-icon"></text>
|
||||
<view>
|
||||
<view class="entry-title">我的定制</view>
|
||||
<view class="entry-desc">查看需求审核及开发进度</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="cuIcon-right entry-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cu-tabbar-height"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createCustomRequest, getUser, listCustomTechStacks } from '@/api/app/index.js'
|
||||
|
||||
export default {
|
||||
name: "basics",
|
||||
data() {
|
||||
return {
|
||||
elements: [{
|
||||
title: '资源库',
|
||||
name: 'resource',
|
||||
color: 'cyan',
|
||||
cuIcon: 'https://img.yidaima.cn/zyk.png'
|
||||
},
|
||||
{
|
||||
title: '手机软件',
|
||||
name: 'phone',
|
||||
color: 'orange',
|
||||
cuIcon: 'https://img.yidaima.cn/sjrj.png'
|
||||
},
|
||||
{
|
||||
title: '电脑软件 ',
|
||||
name: 'software',
|
||||
color: 'mauve',
|
||||
cuIcon: 'https://img.yidaima.cn/dnrj.png'
|
||||
}
|
||||
],
|
||||
};
|
||||
submitPoints: 99,
|
||||
userId: null,
|
||||
integral: 0,
|
||||
submitting: false,
|
||||
techStackLoading: false,
|
||||
techStacks: [],
|
||||
form: { projectName: '', techStackId: null, requirementContent: '' }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
projectNameLength() { return (this.form.projectName || '').length },
|
||||
requirementLength() { return (this.form.requirementContent || '').length }
|
||||
},
|
||||
onShow() {
|
||||
console.log("success")
|
||||
const userInfo = uni.getStorageSync('userInfo') || {}
|
||||
this.userId = userInfo.userId || null
|
||||
if (this.userId) this.loadIntegral()
|
||||
else this.integral = 0
|
||||
this.loadTechStacks()
|
||||
},
|
||||
methods: {
|
||||
loadTechStacks() {
|
||||
this.techStackLoading = true
|
||||
listCustomTechStacks().then(response => {
|
||||
this.techStacks = response.data || []
|
||||
if (!this.techStacks.some(item => item.id === this.form.techStackId)) this.form.techStackId = null
|
||||
}).finally(() => { this.techStackLoading = false })
|
||||
},
|
||||
selectTechStack(id) { this.form.techStackId = id },
|
||||
loadIntegral() {
|
||||
getUser(this.userId).then(response => {
|
||||
this.integral = response && response.user ? Number(response.user.integral || 0) : 0
|
||||
})
|
||||
},
|
||||
goMyCustom() {
|
||||
if (this.ensureLogin()) this.$tab.navigateTo('/pages/mine/custom_requests')
|
||||
},
|
||||
ensureLogin() {
|
||||
if (this.userId) return true
|
||||
uni.showModal({
|
||||
title: '请先登录',
|
||||
content: '登录后才能提交和查看定制需求。',
|
||||
confirmText: '去登录',
|
||||
success: res => { if (res.confirm) uni.switchTab({ url: '/pages/mine/index' }) }
|
||||
})
|
||||
return false
|
||||
},
|
||||
validateForm() {
|
||||
const projectName = (this.form.projectName || '').trim()
|
||||
const requirementContent = (this.form.requirementContent || '').trim()
|
||||
if (projectName.length < 2) {
|
||||
uni.showToast({ title: '项目名称至少填写2个字', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.form.techStackId) {
|
||||
uni.showToast({ title: '请选择技术栈', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (requirementContent.length < 20) {
|
||||
uni.showToast({ title: '项目需求至少填写20个字', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
this.form.projectName = projectName
|
||||
this.form.requirementContent = requirementContent
|
||||
return true
|
||||
},
|
||||
handleSubmit() {
|
||||
if (this.submitting || !this.ensureLogin() || !this.validateForm()) return
|
||||
if (this.integral < this.submitPoints) {
|
||||
uni.showModal({
|
||||
title: '积分不足',
|
||||
content: '当前有' + this.integral + '积分,提交需求需要99积分。',
|
||||
confirmText: '去获取',
|
||||
success: res => { if (res.confirm) uni.switchTab({ url: '/pages/mine/index' }) }
|
||||
})
|
||||
return
|
||||
}
|
||||
this.submitRequest()
|
||||
},
|
||||
submitRequest() {
|
||||
this.submitting = true
|
||||
createCustomRequest(this.form).then(response => {
|
||||
this.integral = Math.max(this.integral - this.submitPoints, 0)
|
||||
this.form.projectName = ''
|
||||
this.form.techStackId = null
|
||||
this.form.requirementContent = ''
|
||||
uni.showModal({
|
||||
title: '提交成功',
|
||||
content: '需求单号:' + (response.data ? response.data.requestNo : '') + ',可在“我的定制”查看状态。',
|
||||
showCancel: false,
|
||||
confirmText: '查看需求',
|
||||
success: () => { this.$tab.navigateTo('/pages/mine/custom_requests') }
|
||||
})
|
||||
}).finally(() => { this.submitting = false })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
page {
|
||||
background-color: #F5F5F5;;
|
||||
}
|
||||
.nav-li {
|
||||
/* padding: 30upx; */
|
||||
border-radius: 24upx;
|
||||
width: 30%;
|
||||
margin: 0 1% 20upx;
|
||||
/* background-image: url(https://i.imgtg.com/2022/12/15/Dsef6.jpg); */
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
background-color: #FFFFFF;
|
||||
z-index: 1;
|
||||
}
|
||||
page { min-height: 100%; background: #f7f7f7; }
|
||||
.custom-page { min-height: 100vh; }
|
||||
.custom-content { padding: 28rpx 28rpx 10rpx; }
|
||||
.service-card { padding: 28rpx; border: 1rpx solid #d9efff; border-radius: 20rpx; background: linear-gradient(135deg, #f0f9ff, #e7f5ff); }
|
||||
.service-head { display: flex; align-items: center; }
|
||||
.service-icon { display: flex; flex-shrink: 0; align-items: center; justify-content: center; width: 82rpx; height: 82rpx; border-radius: 50%; font-size: 42rpx; color: #fff; background: linear-gradient(135deg, #0081ff, #60c2ff); }
|
||||
.service-content { flex: 1; min-width: 0; margin-left: 20rpx; }
|
||||
.service-title { font-size: 34rpx; font-weight: 600; color: #222; }
|
||||
.service-desc { margin-top: 8rpx; font-size: 23rpx; line-height: 1.55; color: #788895; }
|
||||
.flow-row { display: flex; align-items: center; margin-top: 26rpx; padding-top: 22rpx; border-top: 1rpx solid rgba(0, 129, 255, .12); }
|
||||
.flow-item { display: flex; align-items: center; font-size: 23rpx; color: #596c7c; white-space: nowrap; }
|
||||
.flow-number { display: flex; align-items: center; justify-content: center; width: 36rpx; height: 36rpx; margin-right: 8rpx; border-radius: 50%; font-size: 20rpx; color: #0081ff; background: #fff; }
|
||||
.flow-line { flex: 1; height: 1rpx; margin: 0 12rpx; background: #c5e5fb; }
|
||||
.price-card { margin-top: 30rpx; padding: 24rpx; border: 1rpx solid #edf0f2; border-radius: 16rpx; background: #fafbfc; }
|
||||
.price-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx; }
|
||||
.price-title { display: flex; align-items: center; font-size: 30rpx; font-weight: 600; color: #222; }
|
||||
.price-title-icon { margin-right: 12rpx; font-size: 34rpx; color: #ff9700; }
|
||||
.price-stage { padding: 6rpx 12rpx; border-radius: 8rpx; font-size: 20rpx; color: #0081ff; background: #edf7ff; }
|
||||
.price-item { display: flex; align-items: center; min-height: 94rpx; border-bottom: 1rpx solid #f1f1f1; }
|
||||
.price-content { flex: 1; min-width: 0; padding: 16rpx 20rpx 16rpx 0; }
|
||||
.price-name { font-size: 27rpx; font-weight: 500; color: #333; }
|
||||
.price-desc { margin-top: 6rpx; font-size: 21rpx; line-height: 1.45; color: #999; }
|
||||
.price-value { flex-shrink: 0; font-size: 34rpx; font-weight: 600; color: #e76f1a; }
|
||||
.price-value text { margin-right: 2rpx; font-size: 22rpx; font-weight: 500; }
|
||||
.points-value { color: #0081ff; }
|
||||
.points-value text { margin: 0 0 0 5rpx; }
|
||||
.price-tip { display: flex; align-items: flex-start; margin-top: 18rpx; font-size: 21rpx; line-height: 1.5; color: #7b8791; }
|
||||
.price-tip text { margin: 2rpx 10rpx 0 0; color: #0081ff; }
|
||||
.service-note { margin-top: 18rpx; padding: 18rpx 20rpx; border-radius: 12rpx; font-size: 21rpx; line-height: 1.65; color: #856829; background: #fff8e8; }
|
||||
.service-note-title { margin-bottom: 5rpx; font-size: 23rpx; font-weight: 600; color: #7a5917; }
|
||||
.my-custom-entry { display: flex; align-items: center; justify-content: space-between; margin-top: 20rpx; padding: 22rpx 26rpx; border-radius: 18rpx; background: #fff; box-shadow: 0 5rpx 16rpx rgba(50, 50, 50, .035); }
|
||||
.entry-left { display: flex; align-items: center; }
|
||||
.entry-icon { margin-right: 18rpx; font-size: 42rpx; color: #0081ff; }
|
||||
.entry-title { font-size: 28rpx; font-weight: 500; color: #333; }
|
||||
.entry-desc { margin-top: 5rpx; font-size: 22rpx; color: #999; }
|
||||
.entry-arrow { font-size: 30rpx; color: #bbb; }
|
||||
.form-card { margin-top: 24rpx; padding: 30rpx; border-radius: 20rpx; background: #fff; box-shadow: 0 6rpx 18rpx rgba(50, 50, 50, .04); }
|
||||
.form-title-row { display: flex; align-items: center; justify-content: space-between; }
|
||||
.form-title { font-size: 34rpx; font-weight: 600; color: #222; }
|
||||
.title-icon { margin-right: 5rpx; font-size: 40rpx; color: #0081ff; }
|
||||
.field-label { margin-top: 32rpx; font-size: 28rpx; font-weight: 500; color: #333; }
|
||||
.field-label-space { margin-top: 28rpx; }
|
||||
.required { margin-left: 5rpx; color: #e54d42; }
|
||||
.tech-stack-options { display: flex; flex-wrap: wrap; margin: 2rpx -8rpx -8rpx; }
|
||||
.tech-stack-option { box-sizing: border-box; margin: 12rpx 8rpx 0; padding: 14rpx 20rpx; border: 1rpx solid #e3e8ed; border-radius: 10rpx; font-size: 24rpx; line-height: 1.35; color: #65727d; background: #f8fafb; }
|
||||
.tech-stack-selected { border-color: #0081ff; color: #0081ff; background: #edf7ff; box-shadow: 0 0 0 1rpx rgba(0, 129, 255, .08); }
|
||||
.tech-stack-empty { margin-top: 14rpx; padding: 22rpx; border-radius: 12rpx; text-align: center; font-size: 23rpx; color: #999; background: #f8f8f8; }
|
||||
.input-box, .textarea-box { position: relative; box-sizing: border-box; width: 100%; margin-top: 14rpx; border: 1rpx solid #ededed; border-radius: 14rpx; background: #f8f8f8; }
|
||||
.input-box { height: 90rpx; padding: 0 100rpx 0 22rpx; }
|
||||
.input-box input { height: 88rpx; font-size: 28rpx; color: #222; }
|
||||
.textarea-box { min-height: 250rpx; padding: 22rpx 22rpx 50rpx; }
|
||||
.textarea-box textarea { width: 100%; height: 190rpx; font-size: 28rpx; line-height: 1.65; color: #242739; }
|
||||
.input-placeholder { color: #aaa; }
|
||||
.word-count { position: absolute; right: 20rpx; bottom: 28rpx; font-size: 21rpx; color: #aaa; }
|
||||
.textarea-count { bottom: 16rpx; }
|
||||
.rule-box { display: flex; align-items: flex-start; margin-top: 24rpx; padding: 18rpx 20rpx; border-radius: 12rpx; font-size: 23rpx; line-height: 1.65; color: #8b671d; background: #fff8e8; }
|
||||
.rule-icon { margin: 2rpx 12rpx 0 0; color: #ff9700; }
|
||||
.submit-button { margin-top: 30rpx; height: 90rpx; border: 0; border-radius: 12rpx; font-size: 30rpx; font-weight: 600; line-height: 90rpx; color: #fff; background: #0081ff; box-shadow: 0 8rpx 18rpx rgba(0, 129, 255, .18); }
|
||||
.submit-button::after { border: 0; }
|
||||
.submit-button[disabled] { opacity: .62; }
|
||||
.agreement { margin-top: 18rpx; text-align: center; font-size: 21rpx; color: #aaa; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user