You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.vue 21 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. <template>
  2. <div>
  3. <TopHeader :menu="-1"></TopHeader>
  4. <div class="ui container">
  5. <div class="title">您申请的项目将在2030科技项目页面展示</div>
  6. <div class="form-c" v-show="!selectTechPrj">
  7. <div class="form-wrap">
  8. <div class="form-header">申请展示项目</div>
  9. <div class="form-content">
  10. <div class="form-row">
  11. <div class="row-label required">申请项目存放于</div>
  12. <div class="row-content">
  13. <el-radio v-model="form.type" label="openi" @input="changeType()">启智社区</el-radio>
  14. <el-radio v-model="form.type" label="no-openi" @input="changeType()">非启智社区</el-radio>
  15. </div>
  16. </div>
  17. <div class="form-row" :class="form.url_err ? 'form-row-err' : ''">
  18. <div class="row-label required">现项目地址</div>
  19. <div class="row-content">
  20. <el-input size="medium" v-model="form.url" @input="changeUrl" class="can-err"
  21. placeholder='请输入现有项目的 HTTP(s) 或 Git " clone" URL,如:https://openi.pcl.ac.cn/OpenI/aiforge'></el-input>
  22. </div>
  23. </div>
  24. <div v-show="form.type == 'no-openi'">
  25. <div class="form-row" :class="form.alias_err ? 'form-row-err' : ''">
  26. <div class="row-label required baseline">启智项目名称</div>
  27. <div class="row-content">
  28. <div>
  29. <el-input size="medium" v-model="form.repo_alias" @input="changeAlias" class="can-err"></el-input>
  30. </div>
  31. <div class="tips">
  32. 请输入中文、字母、数字和-_ .,最多100个字符。
  33. </div>
  34. </div>
  35. </div>
  36. <div class="form-row" :class="form.name_err ? 'form-row-err' : ''">
  37. <div class="row-label required baseline">启智项目路径</div>
  38. <div class="row-content">
  39. <div class="reop-url-c">
  40. <el-select size="medium" class="owner-sel" v-model="form.uid" @change="changeOwner">
  41. <div slot="prefix" class="owner-item" style="height:100%;padding-left:10px">
  42. <img class="owner-img" :src="ownerSelect.RelAvatarLink">
  43. </div>
  44. <el-option v-for="item in ownerList" :key="item.value" :value="item.value" :label="item.label">
  45. <div class="owner-item">
  46. <img class="owner-img" :src="item.RelAvatarLink">
  47. <span class="owner-name">{{(item.FullName || item.Name)}}</span>
  48. </div>
  49. </el-option>
  50. </el-select>
  51. <span style="margin: 0 8px;font-size:22px;"> / </span>
  52. <el-input size="medium" v-model="form.repo_name" @input="changeRepoName" class="can-err"></el-input>
  53. </div>
  54. <div class="tips">
  55. 启智项目地址:<span class="openi-repo-url">{{ form.repo_url }}</span>
  56. </div>
  57. </div>
  58. </div>
  59. <div class="form-row">
  60. <div class="row-label">关键词</div>
  61. <div class="row-content">
  62. <el-select style="width:100%" size="medium" v-model="form.topics" multiple filterable remote
  63. allow-create default-first-option placeholder="请选择标签" :remote-method="searchTopics"
  64. :loading="form.topicLoading">
  65. <el-option v-for="item in topicsList" :key="item.value" :label="item.label" :value="item.value">
  66. </el-option>
  67. </el-select>
  68. </div>
  69. </div>
  70. <div class="form-row">
  71. <div class="row-label baseline">项目简介</div>
  72. <div class="row-content">
  73. <el-input size="medium" type="textarea" :rows="3" placeholder="请输入项目简介(长度不超过255)" :maxLength="255"
  74. v-model="form.description"></el-input>
  75. </div>
  76. </div>
  77. </div>
  78. <div class="form-row" :class="form.tech_err ? 'form-row-err' : ''">
  79. <div class="row-label required">科技项目</div>
  80. <div class="row-content">
  81. <el-input placeholder="请选择科技项目" size="medium" v-model="form.tech_show" :readonly="true" class="can-err">
  82. <el-button size="medium" slot="append" class="btn-select" @click="goSelectTechPrj">选择科技项目</el-button>
  83. <el-button size="medium" slot="append" class="btn-clear" @click="clearTechPrj">清除</el-button>
  84. </el-input>
  85. </div>
  86. </div>
  87. <div class="form-row" :class="form.institution_err ? 'form-row-err' : ''">
  88. <div class="row-label required">成果贡献单位</div>
  89. <div class="row-content">
  90. <el-select style="width:100%" size="medium" v-model="form.institution" multiple class="can-err">
  91. <el-option v-for="item in institutionList" :key="item.key" :label="item.value"
  92. :value="item.value"></el-option>
  93. </el-select>
  94. </div>
  95. </div>
  96. <div class="form-btn-group">
  97. <el-button size="medium" type="primary" :loading="submitLoading" class="btn confirm-btn"
  98. @click="submit">提交申请</el-button>
  99. <el-button size="medium" class="btn" @click="cancel">{{ $t('cancel') }}</el-button>
  100. </div>
  101. </div>
  102. </div>
  103. </div>
  104. <div class="form-select-tech-prj" v-show="selectTechPrj">
  105. <div class="form-wrap">
  106. <div class="form-header">
  107. <span>选择科技项目</span>
  108. <span>
  109. <el-button class="form-btn-go-back" @click="goBack">返回</el-button>
  110. </span>
  111. </div>
  112. <div class="form-content">
  113. <div class="form-row">
  114. <div class="row-label">请输入科技项目</div>
  115. <div class="row-content">
  116. <el-input placeholder="请输入内容" size="medium" v-model="form.tech_search_keyword"
  117. @keyup.enter.native="searchTechList" class="input-with-select">
  118. <el-select v-model="form.tech_search_sel" style="width:142px" size="medium" slot="prepend"
  119. placeholder="请选择">
  120. <el-option label="项目立项编号" value="0"></el-option>
  121. <el-option label="参与单位" value="1"></el-option>
  122. <el-option label="项目名称" value="2"></el-option>
  123. </el-select>
  124. <el-button size="medium" slot="append" icon="el-icon-search" @click="searchTechList"></el-button>
  125. </el-input>
  126. </div>
  127. </div>
  128. <div class="form-table">
  129. <div style="margin: 0 20px 30px 20px">
  130. <el-table ref="tableRef" border :data="tableData" style="width:100%" v-loading="loading" stripe>
  131. <el-table-column prop="no" label="项目立项编号" align="center" header-align="center"
  132. width="120"></el-table-column>
  133. <el-table-column prop="name" label="科技项目名称" align="center" header-align="center"
  134. width="200"></el-table-column>
  135. <el-table-column prop="institution" label="项目承担单位" align="center" header-align="center"
  136. width="200"></el-table-column>
  137. <el-table-column prop="all_institution" label="所有参与单位" align="center"
  138. header-align="center"></el-table-column>
  139. <el-table-column width="100" label="操作" align="center" header-align="center">
  140. <template slot-scope="scope">
  141. <el-button type="primary" @click="selectedTechPrj(scope.row)">选择</el-button>
  142. </template>
  143. </el-table-column>
  144. </el-table>
  145. </div>
  146. </div>
  147. </div>
  148. </div>
  149. </div>
  150. </div>
  151. </div>
  152. </template>
  153. <script>
  154. import TopHeader from '../components/TopHeader.vue';
  155. import { getTechs, setOpenIApply, setNoOpenIApply, getCreateRepoUser, getCheckRepoName, getTopics } from '~/apis/modules/tech';
  156. export default {
  157. data() {
  158. return {
  159. form: {
  160. type: 'openi',
  161. url: '',
  162. url_err: false,
  163. repo_alias: '',
  164. alias_err: false,
  165. uid: '',
  166. repo_name: '',
  167. name_err: false,
  168. repo_url: '',
  169. topics: [],
  170. topicLoading: false,
  171. description: '',
  172. tech_search_sel: '0',
  173. tech_search_keyword: '',
  174. tech_obj: null,
  175. tech_show: '',
  176. tech_err: false,
  177. institution: [],
  178. institution_err: false,
  179. },
  180. selectTechPrj: false,
  181. loading: false,
  182. tableData: [],
  183. topicsList: [],
  184. institutionList: [],
  185. ownerList: [],
  186. ownerSelect: {},
  187. submitLoading: false,
  188. };
  189. },
  190. components: {
  191. TopHeader,
  192. },
  193. methods: {
  194. resetData() {
  195. this.form.url = '';
  196. this.form.url_err = false;
  197. this.form.repo_alias = '';
  198. this.form.alias_err = false;
  199. this.form.uid = this.ownerList.length ? this.ownerList[0].value : '';
  200. this.form.repo_name = '';
  201. this.form.repo_url = '';
  202. this.form.name_err = false;
  203. this.form.topics = [];
  204. this.form.topicLoading = false;
  205. this.form.description = '';
  206. this.form.tech_search_sel = '0';
  207. this.form.tech_search_keyword = '';
  208. this.form.tech_obj = null;
  209. this.form.tech_show = '';
  210. this.form.tech_err = false;
  211. this.form.institution = [];
  212. this.form.institution_err = false;
  213. this.tableData = [];
  214. this.topicsList = [];
  215. this.institutionList = [];
  216. this.submitLoading = false;
  217. },
  218. changeType() {
  219. this.resetData();
  220. },
  221. checkUrl() {
  222. this.form.url_err = !this.form.url;
  223. return !this.form.url_err;
  224. },
  225. checkRepoAlias() {
  226. const reg = /^[\u4E00-\u9FA5A-Za-z0-9_.-]{1,100}$/;
  227. const res = reg.test(this.form.repo_alias);
  228. this.form.alias_err = !res;
  229. return res;
  230. },
  231. checkRepoName() {
  232. const reg = /^[A-Za-z0-9_.-]{1,100}$/;
  233. const res = reg.test(this.form.repo_name);
  234. this.form.name_err = !res;
  235. return res;
  236. },
  237. checkTech() {
  238. this.form.tech_err = !this.form.tech_obj;
  239. return !this.form.tech_err;
  240. },
  241. checkinstitution() {
  242. this.form.institution_err = this.form.institution.length == 0;
  243. return !this.form.institution_err;
  244. },
  245. changeUrl() {
  246. if (this.form.type == 'openi') return;
  247. const owner = this.ownerSelect.Name;
  248. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  249. const repoValue = this.form.url.match(/^(.*\/)?((.+?)(\.git)?)$/)[3];
  250. this.form.repo_alias = repoValue;
  251. this.checkRepoAlias();
  252. getCheckRepoName({ owner: owner, q: repoValue }).then(res => {
  253. const repo_name = res.data.name;
  254. this.form.repo_name = repo_name;
  255. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  256. this.checkRepoName();
  257. }).catch(err => {
  258. console.log(err);
  259. this.form.repo_name = '';
  260. this.form.repo_url = '';
  261. });
  262. },
  263. changeAlias() {
  264. const owner = this.ownerSelect.Name;
  265. const aliasValue = this.form.repo_alias;
  266. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  267. if (aliasValue && this.checkRepoAlias()) {
  268. getCheckRepoName({ owner: owner, q: aliasValue }).then(res => {
  269. const repo_name = res.data.name;
  270. this.form.repo_name = repo_name;
  271. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  272. }).catch(err => {
  273. console.log(err);
  274. this.form.repo_name = '';
  275. this.form.repo_url = '';
  276. });
  277. } else {
  278. this.form.repo_name = '';
  279. this.form.repo_url = '';
  280. }
  281. },
  282. changeOwner(value) {
  283. this.ownerSelect = this.ownerList.filter(item => item.value == value)[0];
  284. this.form.repo_name && this.changeRepoName();
  285. },
  286. changeRepoName() {
  287. const owner = this.ownerSelect.Name;
  288. const repo_name = this.form.repo_name;
  289. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  290. if (this.checkRepoName()) {
  291. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  292. } else {
  293. this.form.repo_url = '';
  294. }
  295. },
  296. searchTopics(query) {
  297. if (query !== '') {
  298. this.form.topicLoading = true;
  299. getTopics({ q: query }).then(res => {
  300. this.form.topicLoading = false;
  301. const topics = res.data.topics || [];
  302. this.topicsList = topics.map(item => {
  303. return {
  304. value: item.topic_name,
  305. label: item.topic_name,
  306. }
  307. });
  308. }).catch(err => {
  309. this.topicsList = [];
  310. });
  311. } else {
  312. this.topicsList = [];
  313. }
  314. },
  315. goSelectTechPrj() {
  316. this.form.tech_search_sel = '0';
  317. this.form.tech_search_keyword = '';
  318. this.tableData = [];
  319. this.selectTechPrj = true;
  320. this.searchTechList();
  321. },
  322. clearTechPrj() {
  323. this.form.institution = [];
  324. this.institutionList = [];
  325. this.form.tech_show = '';
  326. this.form.tech_obj = null;
  327. this.form.tech_err = false;
  328. },
  329. goBack() {
  330. this.selectTechPrj = false;
  331. },
  332. searchTechList() {
  333. this.loading = true;
  334. getTechs({
  335. no: this.form.tech_search_sel == '0' ? this.form.tech_search_keyword : '',
  336. institution: this.form.tech_search_sel == '1' ? this.form.tech_search_keyword : '',
  337. name: this.form.tech_search_sel == '2' ? this.form.tech_search_keyword : '',
  338. }).then(res => {
  339. this.loading = false;
  340. res = res.data;
  341. this.tableData = res.data || [];
  342. }).catch(err => {
  343. console.log(err);
  344. this.loading = false;
  345. this.tableData = [];
  346. });
  347. },
  348. selectedTechPrj(item) {
  349. this.form.institution = [];
  350. this.institutionList = item.all_institution.split(',').map((item, index) => {
  351. return {
  352. key: item,
  353. value: item,
  354. }
  355. });
  356. this.form.tech_show = `【${item.no}】 ${item.name}`;
  357. this.form.tech_obj = item;
  358. this.checkTech();
  359. this.goBack();
  360. },
  361. submit() {
  362. const subData = {};
  363. let setApi = null;
  364. if (this.form.type == 'openi') {
  365. setApi = setOpenIApply;
  366. const r1 = this.checkUrl();
  367. const r2 = this.checkTech();
  368. const r3 = this.checkinstitution();
  369. if (r1 && r2 && r3) {
  370. subData.url = this.form.url;
  371. subData.no = this.form.tech_obj.no;
  372. subData.institution = this.form.institution.join(',');
  373. } else {
  374. return;
  375. }
  376. } else {
  377. setApi = setNoOpenIApply;
  378. const r1 = this.checkUrl();
  379. const r2 = this.checkTech();
  380. const r3 = this.checkinstitution();
  381. const r4 = this.checkRepoAlias();
  382. const r5 = this.checkRepoName();
  383. if (r1 && r2 && r3 && r4 && r5) {
  384. subData.url = this.form.url;
  385. subData.uid = this.form.uid;
  386. subData.repo_alias = this.form.repo_alias;
  387. subData.repo_name = this.form.repo_name;
  388. subData.topics = [...this.form.topics];
  389. subData.description = this.form.description;
  390. subData.no = this.form.tech_obj.no;
  391. subData.institution = this.form.institution.join(',');
  392. } else {
  393. return;
  394. }
  395. }
  396. // console.log(subData);
  397. // return;
  398. this.submitLoading = true;
  399. setApi(subData).then(res => {
  400. if (res.data && res.data.code == 0) {
  401. this.$message({
  402. type: 'success',
  403. message: '提交成功'
  404. });
  405. setTimeout(() => {
  406. window.location.href = '/tech/tech_view';
  407. }, 2000);
  408. } else {
  409. this.submitLoading = false;
  410. this.$message({
  411. type: 'info',
  412. message: res.data.msg
  413. });
  414. }
  415. }).catch(err => {
  416. this.submitLoading = false;
  417. this.$message({
  418. type: 'error',
  419. message: '提交失败'
  420. });
  421. });
  422. },
  423. cancel() {
  424. window.history.back();
  425. }
  426. },
  427. beforeMount() {
  428. getCreateRepoUser().then(res => {
  429. const data = res.data.Data || [];
  430. this.ownerList = data.map(item => {
  431. return {
  432. value: item.ID,
  433. label: item.FullName || item.Name,
  434. ...item,
  435. }
  436. });
  437. this.ownerSelect = this.ownerList.length ? this.ownerList[0] : {};
  438. this.form.uid = this.ownerList.length ? this.ownerList[0].value : '';
  439. }).catch(err => {
  440. console.log(err);
  441. });
  442. },
  443. mounted() { },
  444. beforeDestroy() { },
  445. };
  446. </script>
  447. <style scoped lang="less">
  448. .title {
  449. text-align: center;
  450. margin: 30px 0;
  451. font-size: 14px;
  452. color: rgb(16, 16, 16);
  453. }
  454. .form-c,
  455. .form-select-tech-prj {
  456. display: flex;
  457. justify-content: center;
  458. .form-wrap {
  459. width: 1000px;
  460. background: rgb(255, 255, 255);
  461. border-color: rgb(212, 212, 213);
  462. border-width: 1px;
  463. border-style: solid;
  464. border-radius: 5px;
  465. box-sizing: border-box;
  466. .form-header {
  467. height: 45px;
  468. background: rgb(240, 240, 240);
  469. border-bottom: 1px solid rgb(212, 212, 213);
  470. color: rgb(16, 16, 16);
  471. padding-left: 16px;
  472. padding-right: 16px;
  473. font-size: 16px;
  474. font-weight: 700;
  475. display: flex;
  476. align-items: center;
  477. justify-content: space-between;
  478. }
  479. .form-content {
  480. margin-top: 40px;
  481. .form-row {
  482. display: flex;
  483. align-items: center;
  484. width: 80%;
  485. margin: 20px auto;
  486. padding-right: 50px;
  487. .row-label {
  488. flex: 2;
  489. text-align: right;
  490. margin-right: 24px;
  491. position: relative;
  492. &.required {
  493. &::after {
  494. position: absolute;
  495. top: -2px;
  496. right: -10px;
  497. content: '*';
  498. color: #db2828;
  499. }
  500. }
  501. &.baseline {
  502. align-self: baseline;
  503. margin-top: 11px;
  504. }
  505. }
  506. .row-content {
  507. flex: 9;
  508. .tips {
  509. margin-top: 4px;
  510. font-size: 14px;
  511. color: rgb(136, 136, 136);
  512. }
  513. }
  514. .reop-url-c {
  515. display: flex;
  516. align-items: center;
  517. }
  518. .openi-repo-url {
  519. color: rgba(16, 16, 16, 1);
  520. }
  521. &.form-row-err {
  522. .row-label {
  523. color: #9f3a38;
  524. }
  525. /deep/ .can-err .el-input__inner {
  526. color: #9f3a38;
  527. background: #fff6f6;
  528. border-color: #e0b4b4;
  529. }
  530. }
  531. }
  532. .form-table {
  533. margin-top: 30px;
  534. margin-bottom: 30px;
  535. /deep/ .el-table__header {
  536. th {
  537. background: rgb(249, 249, 249);
  538. font-size: 12px;
  539. color: rgb(136, 136, 136);
  540. font-weight: normal;
  541. }
  542. }
  543. /deep/ .el-table__body {
  544. td {
  545. font-size: 12px;
  546. }
  547. }
  548. /deep/ .el-radio__label {
  549. display: none;
  550. }
  551. }
  552. .form-btn-group {
  553. display: flex;
  554. justify-content: center;
  555. align-items: center;
  556. margin: 30px 0 40px;
  557. }
  558. }
  559. }
  560. }
  561. .btn {
  562. color: rgb(2, 0, 4);
  563. background-color: rgb(194, 199, 204);
  564. border-color: rgb(194, 199, 204);
  565. &.confirm-btn {
  566. color: #fff;
  567. background-color: rgb(56, 158, 13);
  568. border-color: rgb(56, 158, 13);
  569. margin-right: 20px;
  570. }
  571. }
  572. .btn-select {
  573. background-color: rgb(50, 145, 248) !important;
  574. color: white !important;
  575. border-radius: 0 !important;
  576. height: 35px;
  577. }
  578. .owner-sel {
  579. /deep/ .el-input__inner {
  580. padding-left: 48px;
  581. }
  582. }
  583. .owner-item {
  584. display: flex;
  585. align-items: center;
  586. .owner-img {
  587. width: 24px;
  588. height: 24px;
  589. margin-right: 10px;
  590. }
  591. .owner-name {}
  592. }
  593. </style>