| @@ -69,6 +69,7 @@ your_dashboard = Dashboard | |||
| your_profile = Profile | |||
| your_starred = Starred | |||
| your_settings = Settings | |||
| invite_friends = Invite Friends | |||
| all = All | |||
| sources = Sources | |||
| @@ -69,6 +69,7 @@ your_dashboard=个人中心 | |||
| your_profile=个人信息 | |||
| your_starred=已点赞 | |||
| your_settings=设置 | |||
| invite_friends=邀请好友 | |||
| all=所有 | |||
| sources=自建 | |||
| @@ -180,6 +180,10 @@ | |||
| </i> | |||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||
| </a> | |||
| <a class="item" href="{{AppSubUrl}}/user/invitation_tpl"> | |||
| <i class="icon users"></i> | |||
| {{.i18n.Tr "invite_friends"}} | |||
| </a> | |||
| {{if .IsAdmin}} | |||
| <div class="divider"></div> | |||
| @@ -177,6 +177,10 @@ | |||
| </svg> | |||
| </i> | |||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||
| </a> | |||
| <a class="item" href="{{AppSubUrl}}/user/invitation_tpl"> | |||
| <i class="icon users"></i> | |||
| {{.i18n.Tr "invite_friends"}} | |||
| </a> | |||
| {{if .IsAdmin}} | |||
| <div class="divider"></div> | |||
| @@ -160,6 +160,10 @@ | |||
| </i> | |||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||
| </a> | |||
| <a class="item" href="{{AppSubUrl}}/user/invitation_tpl"> | |||
| <i class="icon users"></i> | |||
| {{.i18n.Tr "invite_friends"}} | |||
| </a> | |||
| {{if .IsAdmin}} | |||
| <div class="divider"></div> | |||
| @@ -181,6 +181,10 @@ | |||
| </i> | |||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||
| </a> | |||
| <a class="item" href="{{AppSubUrl}}/user/invitation_tpl"> | |||
| <i class="icon users"></i> | |||
| {{.i18n.Tr "invite_friends"}} | |||
| </a> | |||
| {{if .IsAdmin}} | |||
| <div class="divider"></div> | |||
| @@ -35,6 +35,9 @@ | |||
| {{if .DisableRegistration}} | |||
| <p>{{.i18n.Tr "auth.disable_register_prompt"}}</p> | |||
| {{else}} | |||
| <div class="field" style="font-weight:400;font-size:14px;color:rgba(250,140,22,1);"> | |||
| <span>您的好友 <span>Itx003</span> 邀请你加入启智社区AI协作平台,畅享充沛的免费算力资源!</span> | |||
| </div> | |||
| <div class="field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> | |||
| <input id="user_name" name="user_name" value="{{.user_name}}" placeholder="{{.i18n.Tr "username"}}" autofocus required> | |||
| </div> | |||
| @@ -71,6 +74,16 @@ | |||
| {{template "user/auth/phone_verify" .}} | |||
| </div> | |||
| {{end}} | |||
| <div class="field"> | |||
| <div style="display:flex;"> | |||
| <div style="display:flex;align-items:center;"> | |||
| <span>推荐人</span> | |||
| </div> | |||
| <input style="flex:1;margin-left:12px;" id="shared_user" name="shared_user" value="{{.password}}" autocomplete="off" /> | |||
| </div> | |||
| </div> | |||
| <div class="field"> | |||
| <div class="ui checkbox"> | |||
| <input name="agree" type="checkbox" tabindex="0" class="hidden" {{if .agree}}checked{{end}}><label>{{.i18n.Tr "use_and_privacy_agree" "/home/term" "/home/privacy" | Safe}}</label> | |||
| @@ -18,6 +18,9 @@ | |||
| v-cloak | |||
| > | |||
| <div> | |||
| <div style="height:60px;"> | |||
| <a href="/user/invitation_tpl"><img src="/img/ad/ad02.png" style="width:100%;height:100%" /></a> | |||
| </div> | |||
| <div v-if="!isOrganization" class="ui two item tabable menu"> | |||
| <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{.i18n.Tr "repository"}}</a> | |||
| <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{.i18n.Tr "organization"}}</a> | |||
| @@ -48,6 +48,12 @@ | |||
| </li> | |||
| {{end}} | |||
| {{end}} | |||
| <li> | |||
| {{svg "octicon-clock" 16}} {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}} | |||
| <div style="margin-top:6px;height:50px;"> | |||
| <a href="/user/invitation_tpl"><img src="/img/ad/ad02.png" style="width:100%;height:100%" /></a> | |||
| </div> | |||
| </li> | |||
| <li>{{svg "octicon-clock" 16}} {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li> | |||
| {{if and .Orgs .HasOrgsVisible}} | |||
| @@ -0,0 +1,7 @@ | |||
| {{template "base/head" .}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-user-invite.css?v={{MD5 AppVer}}" /> | |||
| <div class="user settings invite"> | |||
| <div id="__vue-root"></div> | |||
| </div> | |||
| <script src="{{StaticUrlPrefix}}/js/vp-user-invite.js?v={{MD5 AppVer}}"></script> | |||
| {{template "base/footer" .}} | |||
| @@ -0,0 +1,87 @@ | |||
| ; (function () { | |||
| const adList = [{ | |||
| id: 1, | |||
| pos: { | |||
| left: 50, | |||
| bottom: 50, | |||
| }, | |||
| src: '/img/ad/ad01.png', | |||
| url: '/user/invitation_tpl', | |||
| width: 144, | |||
| height: 108, | |||
| }/*, { | |||
| id: 2, | |||
| pos: { | |||
| right: 50, | |||
| bottom: 50, | |||
| }, | |||
| src: '/img/ad/ad01.png', | |||
| url: '/user/invitation_tpl', | |||
| width: 144, | |||
| height: 108, | |||
| }*/]; | |||
| function createAd(adList) { | |||
| const adInfoStr = window.localStorage.getItem('ads') || '{}'; | |||
| let adInfoObj = JSON.parse(adInfoStr); | |||
| const today = new Date(); | |||
| const timeEnd = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1).getTime(); | |||
| const now = Date.now(); | |||
| if (!adInfoObj.expires || adInfoObj.expires <= now) { | |||
| adInfoObj = { | |||
| expires: timeEnd, | |||
| }; | |||
| } | |||
| for (var i = 0, iLen = adList.length; i < iLen; i++) { | |||
| var adI = adList[i]; | |||
| var showOr = adInfoObj[adI.id] === false ? false : true; | |||
| adInfoObj[adI.id] = showOr; | |||
| if (!showOr) continue; | |||
| var adEl = $(`<div class="__ad_c__" _id="${adI.id}" style="position:fixed;z-index:99999999; | |||
| width:${adI.width}px;height:${adI.height}px; | |||
| left:${adI.pos.left !== undefined ? adI.pos.left + 'px' : ''}; | |||
| top:${adI.pos.top !== undefined ? adI.pos.top + 'px' : ''}; | |||
| right:${adI.pos.right !== undefined ? adI.pos.right + 'px' : ''}; | |||
| bottom:${adI.pos.bottom !== undefined ? adI.pos.bottom + 'px' : ''};"> | |||
| <a style="" href="${adI.url}" target="_blank"> | |||
| <img style="height:100%;width:100%;" src="${adI.src}" /> | |||
| </a> | |||
| <div class="__ad_close_c__" style="position:absolute;top:6px;right:6px;"> | |||
| <i class="ri-close-circle-line __ad_close__" style="color:white;font-size:18px;cursor:pointer;"></i> | |||
| </div> | |||
| </div>`); | |||
| $('body').append(adEl); | |||
| } | |||
| window.localStorage.setItem('ads', JSON.stringify(adInfoObj)); | |||
| } | |||
| function initAdEvent() { | |||
| $('body').on('click', '.__ad_c__ .__ad_close__', function () { | |||
| var self = $(this); | |||
| var adEl = self.closest('.__ad_c__'); | |||
| var adId = adEl.attr('_id'); | |||
| const adInfoStr = window.localStorage.getItem('ads') || '{}'; | |||
| const adInfoObj = JSON.parse(adInfoStr); | |||
| adInfoObj[adId] = false; | |||
| window.localStorage.setItem('ads', JSON.stringify(adInfoObj)); | |||
| adEl.remove(); | |||
| }); | |||
| var scrollTopOld = $(document).scrollTop(); | |||
| var timeHandler = null; | |||
| $(window).scroll(function (e) { | |||
| var scrollTop = $(document).scrollTop(); | |||
| var offSet = scrollTop - scrollTopOld; | |||
| scrollTopOld = scrollTop; | |||
| timeHandler && clearTimeout(timeHandler); | |||
| $('.__ad_c__').animate({ bottom: 50 + offSet + 'px' }, 0); | |||
| timeHandler = setTimeout(function () { | |||
| $('.__ad_c__').animate({ bottom: 50 + 'px' }, 0); | |||
| }, 20); | |||
| }); | |||
| } | |||
| setTimeout(function () { | |||
| createAd(adList); | |||
| initAdEvent(); | |||
| }, 0); | |||
| })(); | |||
| @@ -52,6 +52,7 @@ import router from "./router/index.js"; | |||
| import { Message } from "element-ui"; | |||
| import { i18nVue } from "./features/i18nVue.js"; | |||
| import './features/ad.js'; | |||
| Vue.use(ElementUI); | |||
| Vue.prototype.$axios = axios; | |||
| @@ -77,7 +77,7 @@ | |||
| }); | |||
| function mouseMove(e) { | |||
| var _clientX = e.clientX; | |||
| var _clientX = e.clientX !== undefined ? e.clientX : e.targetTouches[0].clientX; | |||
| var offset = _clientX - clientX; | |||
| var triggerEl = self.dom.find('.slide-trigger'); | |||
| var triggerWidth = triggerEl.width(); | |||
| @@ -99,6 +99,8 @@ | |||
| function mouseUp(e) { | |||
| $(document).off('mousemove', mouseMove); | |||
| $(document).off('mouseup', mouseUp); | |||
| $(document).off('touchmove', mouseMove); | |||
| $(document).off('touchend', mouseUp); | |||
| self.isMoving = false; | |||
| $.ajax({ | |||
| url: '/verifySlideImage', | |||
| @@ -106,7 +108,7 @@ | |||
| dataType: 'json', | |||
| data: { | |||
| slide_id: self.imgID, | |||
| x: parseInt(self.dom.find('.slide-image-small').position().left) | |||
| x: parseInt(self.dom.find('.slide-image-small').position().left / self.dom.find('.slide-image-big').attr('scale')) | |||
| }, | |||
| success: function (res) { | |||
| if (res && res.Code === 0) { | |||
| @@ -138,13 +140,18 @@ | |||
| }); | |||
| } | |||
| this.dom.find('.slide-trigger').on('mousedown', function (e) { | |||
| function mouseDown(e) { | |||
| if (self.verifySucess) return; | |||
| clientX = e.clientX; | |||
| clientX = e.clientX !== undefined ? e.clientX : e.targetTouches[0].clientX; | |||
| oLeft = $(this).position().left; | |||
| $(document).on('mousemove', mouseMove); | |||
| $(document).on('mouseup', mouseUp); | |||
| }); | |||
| $(document).on('touchmove', mouseMove); | |||
| $(document).on('touchend', mouseUp); | |||
| } | |||
| this.dom.find('.slide-trigger').on('mousedown', mouseDown); | |||
| this.dom.find('.slide-trigger').on('touchstart', mouseDown); | |||
| this.dom.find('.verify-code-send-btn').on('click', function () { | |||
| if (!self.canSendCode) return; | |||
| @@ -199,6 +206,7 @@ | |||
| self.dom.find('.slide-bar-wrap').css('display', 'flex'); | |||
| self.dom.find('.verify-code-c').css('display', 'flex'); | |||
| self.dom.find('.modify-phone-number').hide(); | |||
| self.refreshImages(); | |||
| }); | |||
| }; | |||
| @@ -210,6 +218,8 @@ | |||
| this.imgID = ''; | |||
| this.dom.find('.slide-bar').removeClass('sucess error').css('width', '30px'); | |||
| this.dom.find('.slide-trigger').removeClass('sucess error').css('left', '0px'); | |||
| var scale = this.dom.find('.slide-bar-bg').width() / 391; | |||
| this.dom.find('.slide-image-big').css('transform', `scale(${scale})`).attr('scale', scale); | |||
| this.dom.find('.slide-trigger .icon').hide(); | |||
| this.dom.find('.slide-trigger .icon.arrow').show(); | |||
| this.dom.find('.slide-txt').show(); | |||
| @@ -174,10 +174,11 @@ | |||
| width: 391px; | |||
| height: 196px; | |||
| top: 36px; | |||
| left: 0; | |||
| left: 1px; | |||
| border-radius: 2px; | |||
| z-index: 100; | |||
| display: none; | |||
| transform-origin: 0 0; | |||
| } | |||
| .__phone-verify-code .slide-bar-c .slide-image-small { | |||
| @@ -0,0 +1,11 @@ | |||
| import service from '../service'; | |||
| // 邀请好友页面数据 | |||
| export const getUserInvitationCode = () => { | |||
| return service({ | |||
| url: '/user/invitation_code', | |||
| method: 'get', | |||
| params: {}, | |||
| data: {}, | |||
| }); | |||
| } | |||
| @@ -0,0 +1,279 @@ | |||
| <template> | |||
| <div class="ui container"> | |||
| <div class="title"> | |||
| <div class="title-1"><span>邀请好友</span></div> | |||
| <div class="title-2"><span>复制二维码或者邀请注册链接分享给好友</span></div> | |||
| </div> | |||
| <div class="content-1"> | |||
| <div class="img-c"> | |||
| <img class="img" src="/img/ad/ad03.jpg" /> | |||
| <div class="txt">邀请好友来启智,用免费算力还能赚奖金!</div> | |||
| </div> | |||
| <div class="descr"> | |||
| <span>新一期的开源打榜活动,每邀请一名好友注册并激活,就可以获得5打榜积分。快快邀请更多好友帮你冲击榜单吧~ </span> | |||
| <a>点击查看活动详情</a> | |||
| </div> | |||
| </div> | |||
| <div class="content-2"> | |||
| <div class="txt-c"> | |||
| <div class="txt-1"> | |||
| <span>启智AI协作平台是启智社区面向AI开发者提供的一站式AI开发协作平台,提供了代码托管、数据集管理、基于异构计算资源的模型调试与训练等功能。目前已经与鹏城云脑、中国算力网(C²NET)一期打通,免费提供丰富算力资源,支撑大家完成AI开发任务。</span> | |||
| </div> | |||
| <div class="txt-2"><span>{{ sharedLink }}</span></div> | |||
| <div class="txt-3"><span>推荐人:</span><span>{{ sharedUser }}</span></div> | |||
| <el-button class="__copy_link_btn__" type="primary">复制注册邀请链接</el-button> | |||
| </div> | |||
| <div class="qr-code"> | |||
| <img src="" alt="" style="width:120px;height:120px;"> | |||
| </div> | |||
| </div> | |||
| <div class="table-container"> | |||
| <div> | |||
| <el-table border :data="tableData" style="width:100%" v-loading="loading" stripe> | |||
| <el-table-column prop="ID" label="已邀请好友" align="left" header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <div style="display:flex;align-items:center;padding-left:20px;"> | |||
| <img src="" alt="" style="height:45px;width:45px;margin-right:10px;" /> | |||
| <a :href="scope.row.userLink" style="font-weight:500;font-size:15px;">{{ scope.row.userName }}</a> | |||
| </div> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="statusStr" label="状态" align="center" header-align="center"> | |||
| <template slot-scope="scope"> | |||
| <span :style="{ color: scope.row.statusColor }">{{ scope.row.statusStr }}</span> | |||
| </template> | |||
| </el-table-column> | |||
| <el-table-column prop="regTime" label="注册时间" align="center" header-align="center"> | |||
| </el-table-column> | |||
| <template slot="empty"> | |||
| <span style="font-size: 12px">{{ | |||
| loading ? $t('loading') : $t('noData') | |||
| }}</span> | |||
| </template> | |||
| </el-table> | |||
| </div> | |||
| <div class="__r_p_pagination"> | |||
| <div style="margin-top: 2rem"> | |||
| <div class="center"> | |||
| <el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage" | |||
| :page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize" | |||
| layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total"> | |||
| </el-pagination> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import { formatDate } from 'element-ui/lib/utils/date-util'; | |||
| import Clipboard from 'clipboard'; | |||
| export default { | |||
| data() { | |||
| return { | |||
| sharedLink: 'https://git.openi.org.cn/user/sign_up?sharedUser=Openihu', | |||
| sharedUser: 'Openihu', | |||
| loading: false, | |||
| tableData: [], | |||
| pageInfo: { | |||
| curpage: 1, | |||
| pageSize: 10, | |||
| pageSizes: [10], | |||
| total: 0, | |||
| }, | |||
| }; | |||
| }, | |||
| components: {}, | |||
| methods: { | |||
| initCopy() { | |||
| const clipboard = new Clipboard('.__copy_link_btn__', { | |||
| text: () => { | |||
| return this.sharedLink; | |||
| }, | |||
| }) | |||
| clipboard.on('success', (e) => { | |||
| this.$message({ | |||
| type: 'success', | |||
| message: '分享内容已复制到剪切板' | |||
| }); | |||
| }); | |||
| }, | |||
| getTableData() { | |||
| const data = new Array(10).fill(0).map((item, index) => { | |||
| const status = Math.random() > 0.5 ? '1' : '2'; | |||
| return { | |||
| userName: 'userName-' + index + '-' + Math.random().toFixed(2), | |||
| userLink: 'userLink-' + index + '-' + Math.random().toFixed(2), | |||
| status: status, | |||
| statusStr: status == '1' ? '已激活' : '未激活', | |||
| statusColor: status == '1' ? 'rgb(82, 196, 26)' : 'rgb(245, 34, 45)', | |||
| regTime: 'regTime' + index + Math.random().toFixed(2), | |||
| } | |||
| }); | |||
| this.tableData = data; | |||
| return; | |||
| const params = { | |||
| page: this.pageInfo.curpage, | |||
| pagesize: this.pageInfo.pageSize, | |||
| }; | |||
| this.loading = true; | |||
| getResSceneList(params).then(res => { | |||
| this.loading = false; | |||
| res = res.data; | |||
| if (res.Code === 0) { | |||
| const list = res.Data.List; | |||
| const data = list.map((item) => { | |||
| return { | |||
| } | |||
| }); | |||
| this.tableData = data; | |||
| this.pageInfo.total = res.Data.TotalSize; | |||
| } | |||
| }).catch(err => { | |||
| console.log(err); | |||
| this.loading = false; | |||
| }); | |||
| }, | |||
| currentChange(val) { | |||
| this.pageInfo.curpage = val; | |||
| this.getTableData(); | |||
| }, | |||
| }, | |||
| mounted() { | |||
| this.initCopy(); | |||
| this.getTableData(); | |||
| }, | |||
| beforeDestroy() { | |||
| }, | |||
| }; | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .title { | |||
| margin-top: 15px; | |||
| margin-bottom: 15px; | |||
| .title-1 { | |||
| font-weight: 500; | |||
| font-size: 20px; | |||
| color: rgba(16, 16, 16, 1); | |||
| margin-bottom: 10px; | |||
| } | |||
| .title-2 { | |||
| font-weight: 400; | |||
| font-size: 14px; | |||
| color: rgba(136, 136, 136, 1); | |||
| } | |||
| } | |||
| .content-1 { | |||
| margin-bottom: 32px; | |||
| .img-c { | |||
| height: 80px; | |||
| position: relative; | |||
| .img { | |||
| width: 100%; | |||
| height: 100%; | |||
| } | |||
| .txt { | |||
| position: absolute; | |||
| width: 100%; | |||
| height: 100%; | |||
| left: 0; | |||
| top: 0; | |||
| line-height: 80px; | |||
| padding-left: 25px; | |||
| font-weight: 500; | |||
| font-size: 24px; | |||
| color: rgb(255, 255, 255); | |||
| } | |||
| } | |||
| .descr { | |||
| font-weight: 300; | |||
| font-size: 16px; | |||
| color: rgb(16, 16, 16); | |||
| padding: 25px; | |||
| border-left: 1px solid rgba(0, 0, 0, 0.1); | |||
| border-right: 1px solid rgba(0, 0, 0, 0.1); | |||
| border-bottom: 1px solid rgba(0, 0, 0, 0.1); | |||
| border-radius: 0px 0px 4px 4px; | |||
| } | |||
| } | |||
| .content-2 { | |||
| display: flex; | |||
| background-color: rgb(228, 242, 255); | |||
| border-color: rgb(228, 242, 255); | |||
| border-width: 1px; | |||
| border-style: solid; | |||
| border-radius: 5px; | |||
| padding: 25px; | |||
| margin-bottom: 32px; | |||
| .txt-c { | |||
| flex: 1; | |||
| font-weight: 300; | |||
| font-size: 16px; | |||
| color: rgb(16, 16, 16); | |||
| span { | |||
| line-height: 24px; | |||
| } | |||
| div { | |||
| margin-bottom: 6px; | |||
| } | |||
| .txt-3 { | |||
| margin-bottom: 15px; | |||
| } | |||
| } | |||
| .qr-code { | |||
| width: 150px; | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: end; | |||
| } | |||
| } | |||
| .table-container { | |||
| margin-bottom: 16px; | |||
| /deep/ .el-table__header { | |||
| th { | |||
| background: rgb(245, 245, 246); | |||
| font-size: 12px; | |||
| color: rgb(36, 36, 36); | |||
| } | |||
| } | |||
| /deep/ .el-table__body { | |||
| td { | |||
| font-size: 12px; | |||
| } | |||
| } | |||
| .op-btn { | |||
| cursor: pointer; | |||
| font-size: 12px; | |||
| color: rgb(25, 103, 252); | |||
| margin: 0 5px; | |||
| } | |||
| } | |||
| .center { | |||
| display: flex; | |||
| justify-content: center; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,17 @@ | |||
| import Vue from 'vue'; | |||
| import ElementUI from 'element-ui'; | |||
| import 'element-ui/lib/theme-chalk/index.css'; | |||
| import localeEn from 'element-ui/lib/locale/lang/en'; | |||
| import localeZh from 'element-ui/lib/locale/lang/zh-CN'; | |||
| import { i18n, lang } from '~/langs'; | |||
| import App from './index.vue'; | |||
| Vue.use(ElementUI, { | |||
| locale: lang === 'zh-CN' ? localeZh : localeEn, | |||
| size: 'small', | |||
| }); | |||
| new Vue({ | |||
| i18n, | |||
| render: (h) => h(App), | |||
| }).$mount('#__vue-root'); | |||