ProjectCreate.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. <template>
  2. <div class="create-container">
  3. <div class="title h1">申请项目</div>
  4. <div class="create-body">
  5. <el-form :model="project" :rules="rules" ref="project" label-width="12%" class="demo-project">
  6. <el-form-item label="项目名称" prop="name">
  7. <el-input v-model="project.name"></el-input>
  8. </el-form-item>
  9. <el-form-item label="联系方式" prop="contact">
  10. <div>
  11. <el-row :gutter="2">
  12. <el-col :span="2">
  13. <span>联系人</span>
  14. </el-col>
  15. <el-col :span="10">
  16. <el-input v-model="project.contactName" placeholder="请输入联系人姓名"></el-input>
  17. </el-col>
  18. </el-row>
  19. <el-row :gutter="2">
  20. <el-col :span="2">
  21. <span>联系人电话</span>
  22. </el-col>
  23. <el-col :span="10">
  24. <el-input v-model="project.contactPhone" placeholder="请输入联系人电话"></el-input>
  25. </el-col>
  26. </el-row>
  27. </div>
  28. </el-form-item>
  29. <el-form-item label="平台" prop="platform">
  30. <el-checkbox-group v-model="project.platform">
  31. <el-checkbox label="0">IOS</el-checkbox>
  32. <el-checkbox label="1">ANDROID</el-checkbox>
  33. <el-checkbox label="2">WEB</el-checkbox>
  34. </el-checkbox-group>
  35. </el-form-item>
  36. <el-form-item label="需求描述" prop="desc">
  37. <el-input type="textarea" v-model="project.desc"></el-input>
  38. </el-form-item>
  39. <el-form-item label="价格" prop="price">
  40. <el-input type="number" v-model="project.price">
  41. <template slot="append">¥</template>
  42. </el-input>
  43. </el-form-item>
  44. <el-form-item label="服务类型" prop="type">
  45. <el-checkbox-group v-model="project.type">
  46. <el-checkbox label="0" name="0">接口测试</el-checkbox>
  47. <el-checkbox label="1" name="1">安全漏洞扫描</el-checkbox>
  48. <el-checkbox label="2" name="2">风险评估服务</el-checkbox>
  49. <el-checkbox label="3" name="3">源代码安全审计服务</el-checkbox>
  50. <el-checkbox label="4" name="4">功能测试服务</el-checkbox>
  51. <el-checkbox label="5" name="5">性能测试</el-checkbox>
  52. <el-checkbox label="6" name="6">功能和易用性测试</el-checkbox>
  53. </el-checkbox-group>
  54. </el-form-item>
  55. <el-form-item label="用途" prop="usage">
  56. <el-input v-model="project.usage"></el-input>
  57. </el-form-item>
  58. <el-form-item label="预算" prop="budget">
  59. <el-input type="number" v-model="project.budget">
  60. <template slot="append">¥</template>
  61. </el-input>
  62. </el-form-item>
  63. <el-form-item label="项目可见性" prop="resource">
  64. <el-tabs
  65. :tab-position="tabPosition"
  66. v-model="project.resource"
  67. style="max-height: 200px;"
  68. >
  69. <el-tab-pane :label="resourceType[0]" name="0">
  70. <el-radio-group v-model="project.institution">
  71. <el-radio
  72. :label="item"
  73. name="type"
  74. v-for="item,index in institutionArray"
  75. :key="index"
  76. ></el-radio>
  77. </el-radio-group>
  78. </el-tab-pane>
  79. <el-tab-pane :label="resourceType[1]" name="1">
  80. <provincecity
  81. ref="addFormProvince"
  82. @selectChange="locationChange"
  83. :provinceCode="project.location.provinceCode"
  84. :cityCode="project.location.cityCode"
  85. ></provincecity>
  86. </el-tab-pane>
  87. <el-tab-pane :label="resourceType[2]" name="2"></el-tab-pane>
  88. </el-tabs>
  89. </el-form-item>
  90. <el-form-item label="需求文档" prop="doc">
  91. <el-upload
  92. drag
  93. class="upload-demo"
  94. action=""
  95. :on-remove="handleRemove"
  96. :before-remove="beforeRemove"
  97. :limit="1"
  98. :on-exceed="handleExceed"
  99. :before-upload="beforeFileUpload"
  100. :http-request="uploadRequireDoc"
  101. :file-list="project.doc"
  102. >
  103. <i class="el-icon-upload"></i>
  104. <div class="el-upload__text">
  105. 将文件拖到此处,或
  106. <em>点击上传</em>
  107. </div>
  108. <div class="el-upload__tip" slot="tip">请上传需求文档</div>
  109. </el-upload>
  110. </el-form-item>
  111. <el-form-item label="安装包" prop="file">
  112. <el-upload
  113. drag
  114. class="upload-demo"
  115. action=""
  116. :on-remove="handleRemove"
  117. :before-remove="beforeRemove"
  118. :limit="1"
  119. :on-exceed="handleExceed"
  120. :before-upload="beforeFileUpload"
  121. :http-request="uploadApkFile"
  122. :file-list="project.file"
  123. >
  124. <i class="el-icon-upload"></i>
  125. <div class="el-upload__text">
  126. 将文件拖到此处,或
  127. <em>点击上传</em>
  128. </div>
  129. <div class="el-upload__tip" slot="tip">只能上传Android或IOS安装包文件</div>
  130. </el-upload>
  131. </el-form-item>
  132. <el-form-item label="项目截止时间" prop="datetime">
  133. <div class="block">
  134. <el-date-picker
  135. v-model="project.datetime"
  136. type="datetime"
  137. placeholder="选择截止时间"
  138. align="right"
  139. :picker-options="pickerOptions"
  140. ></el-date-picker>
  141. </div>
  142. </el-form-item>
  143. <el-form-item>
  144. <div class="btn btn-medium btn-info" v-on:click="submitForm('project')">立即申请</div>
  145. <div class="btn btn-medium" @click="resetForm('project')">重置</div>
  146. </el-form-item>
  147. </el-form>
  148. </div>
  149. </div>
  150. </template>
  151. <script>
  152. import Enum from '@/constants/enum/index'
  153. import PlatformType from '@/constants/enum/platform-type'
  154. import Http from '@/js/http.js'
  155. import Apis from '@/js/api.js'
  156. import provincecity from '@/components/commons/ProvinceCity'
  157. import provinceCityJSON from '@/constants/provinceCity.json'
  158. import ServiceType from '@/constants/enum/service-type'
  159. import ResourceType from '@/constants/enum/resource-type'
  160. export default {
  161. name: 'ProjectCreate',
  162. components: {
  163. provincecity
  164. },
  165. data () {
  166. var validatePass = (rule, value, callback) => {
  167. var reg = /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/
  168. if (this.contactPhone) {
  169. if (!reg.test(this.contactPhone)) {
  170. callback(new Error('请检查手机号码'))
  171. } else {
  172. callback()
  173. }
  174. }
  175. }
  176. return {
  177. tabPosition: 'top',
  178. institutionArray: Enum.institution,
  179. platformType: [],
  180. serviceType: ServiceType,
  181. resourceType: ResourceType,
  182. project: {
  183. userId: 3,
  184. name: '',
  185. contactName: '',
  186. contactPhone: '',
  187. type: [],
  188. platform: [],
  189. desc: '',
  190. doc: '123',
  191. file: '123',
  192. resource: '0',
  193. location: {provinceCode: '3200', cityCode: '3201'},
  194. institution: '',
  195. datetime: '',
  196. price: '',
  197. usage: '',
  198. budget: ''
  199. },
  200. pickerOptions: {
  201. shortcuts: [
  202. {
  203. text: '今天',
  204. onClick (picker) {
  205. picker.$emit('pick', new Date())
  206. }
  207. },
  208. {
  209. text: '昨天',
  210. onClick (picker) {
  211. const date = new Date()
  212. date.setTime(date.getTime() - 3600 * 1000 * 24)
  213. picker.$emit('pick', date)
  214. }
  215. },
  216. {
  217. text: '一周前',
  218. onClick (picker) {
  219. const date = new Date()
  220. date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
  221. picker.$emit('pick', date)
  222. }
  223. }
  224. ]
  225. },
  226. rules: {
  227. name: [
  228. {required: true, message: '请输入项目名称', trigger: 'blur'}
  229. // { min: 3, max: 5, message: "长度在 3 到 5 个字符", trigger: "blur" }
  230. ],
  231. type: [
  232. {
  233. type: 'array',
  234. required: true,
  235. message: '请至少选择一种服务类型',
  236. trigger: 'change'
  237. }
  238. ],
  239. platform: [
  240. {
  241. type: 'array',
  242. required: true,
  243. message: '请至少选择一个平台',
  244. trigger: 'change'
  245. }
  246. ],
  247. desc: [{required: true, message: '请填写活动形式', trigger: 'blur'}],
  248. contact: [{validator: validatePass, trigger: 'blur'}],
  249. resource: [
  250. {
  251. required: true,
  252. message: '请选择项目可见性',
  253. trigger: 'change'
  254. }
  255. ]
  256. }
  257. }
  258. },
  259. mounted () {
  260. this.$nextTick(() => {
  261. this.init()
  262. })
  263. },
  264. watch: {
  265. 'project.institution' () {
  266. if (this.project.institution) {
  267. this.$refs.addFormProvince.resetProviceCity()
  268. this.project.location = {provinceCode: '', cityCode: ''}
  269. }
  270. },
  271. 'project.location' () {
  272. if (this.project.location.provinceCode || this.project.location.cityCode) {
  273. this.project.institution = ''
  274. }
  275. },
  276. 'project.resource' () {
  277. if (this.project.resource == '2') {
  278. this.$refs.addFormProvince.resetProviceCity()
  279. this.project.institution = ''
  280. this.project.location = {provinceCode: '', cityCode: ''}
  281. }
  282. },
  283. deep: true
  284. },
  285. methods: {
  286. updateLocation (location) {
  287. var provinceName = ''
  288. var cityName = ''
  289. for (var item of provinceCityJSON.provinces) {
  290. if (item.code === location.provinceCode) {
  291. provinceName = item.name
  292. for (var city of item.cities) {
  293. if (city.code === location.cityCode) {
  294. cityName = city.name
  295. break
  296. }
  297. }
  298. }
  299. }
  300. return provinceName + ' / ' + cityName
  301. },
  302. locationChange (provinceId, cityId) {
  303. if (provinceId || cityId) {
  304. this.project.location = {provinceCode: provinceId, cityCode: cityId}
  305. }
  306. },
  307. init () {
  308. this.project.platform.map(item => {
  309. this.platformType.push(PlatformType[item])
  310. })
  311. },
  312. submitForm (formName) {
  313. Http.post(Apis.PROJECT.CREATE_PROJECT, this.project).then((res) => {
  314. console.log(res.status)
  315. if (window.history.length <= 1) {
  316. this.$router.push({path: '/'})
  317. return false
  318. } else {
  319. this.$router.go(-1)
  320. }
  321. })
  322. // this.$refs[formName].validate(valid => {
  323. // if (valid) {
  324. // Http.post(Apis.PROJECT.CREATE_PROJECT, this.project).then((res) => {
  325. // console.log(res.status)
  326. // })
  327. // //提交 project
  328. // } else {
  329. // console.log('error submit!!')
  330. // return false
  331. // }
  332. // })
  333. },
  334. resetForm (formName) {
  335. this.$refs[formName].resetFields()
  336. this.project.name = ''
  337. this.project.type = []
  338. this.project.platform = []
  339. this.project.desc = ''
  340. this.project.file = ''
  341. this.project.doc = ''
  342. this.project.contactName = ''
  343. this.project.contactPhone = ''
  344. this.project.resource = '非定向'
  345. this.project.institution = ''
  346. this.project.datetime = ''
  347. this.project.price = ''
  348. this.project.usage = ''
  349. this.project.budget = ''
  350. },
  351. beforeFileUpload (file) {
  352. // const isAPK = file.type === 'application/vnd.android.package-archive'
  353. // const isDMG = file.type === 'application/octet-stream'
  354. //
  355. // if (!isAPK && !isDMG) {
  356. // this.$message.error('上传安装包只能是 APK 或 DMG 格式!')
  357. // }
  358. // return isAPK && isDMG
  359. },
  360. loadData () {
  361. Http.get(Apis.PAGE.PROJECT_DETAIL_PAGE).then((res) => {
  362. this.project = res.project
  363. })
  364. },
  365. handleRemove (file, fileList) {
  366. console.log(file, fileList)
  367. },
  368. handleExceed (files, fileList) {
  369. this.$message.warning(
  370. `当前限制选择 1 个文件,本次选择了 ${
  371. files.length
  372. } 个文件,共选择了 ${files.length + fileList.length} 个文件`
  373. )
  374. },
  375. beforeRemove (file, fileList) {
  376. return this.$confirm(`确定移除 ${file.name}?`)
  377. },
  378. uploadRequireDoc (param) {
  379. const formData = new FormData()
  380. let config = {
  381. //添加请求头
  382. headers: {'Content-Type': 'multipart/form-data'},
  383. }
  384. formData.append('file', param.file)
  385. Http.upload(Apis.FILE.REQUIREMENT_FILE.replace('{userId}', 3), formData, config).then((res) => {
  386. console.log('上传成功')
  387. this.project.requireDocUrl = res.data
  388. console.log(res.data)
  389. })
  390. },
  391. uploadApkFile (param) {
  392. const formData = new FormData()
  393. let config = {
  394. //添加请求头
  395. headers: {'Content-Type': 'multipart/form-data'},
  396. }
  397. formData.append('file', param.file)
  398. Http.upload(Apis.FILE.APK.replace('{userId}', 3), formData, config).then((res) => {
  399. console.log('上传成功')
  400. this.project.fileUrl = res.data
  401. console.log(res)
  402. })
  403. }
  404. }
  405. }
  406. </script>
  407. <style lang="less" scoped>
  408. .el-col {
  409. padding: 0 !important;
  410. }
  411. .el-row {
  412. margin-bottom: 10px;
  413. }
  414. .el-radio {
  415. margin: 10px 20px 10px 0;
  416. }
  417. .el-form-item /deep/ .el-tabs__content {
  418. max-height: 120px !important;
  419. overflow: auto;
  420. }
  421. </style>