Authored by baoss

Merge branch 'develop' of git.yoho.cn:fe/xianyu-ufo-app-web into develop

... ... @@ -11,6 +11,7 @@ const serviceApi = global.yoho.ServiceAPI;
const checkApiMap = url => {
return apiMaps[url] ? apiMaps[url] : void 0;
};
// eslint-disable-next-line space-before-function-paren
const request = async ({ url, method, reqParams = {}, context }) => {
const apiInfo = checkApiMap(url);
... ...
... ... @@ -18,6 +18,7 @@ const $app = document.getElementById('app');
const isDegrade = Boolean(!($app && $app.attributes['data-server-rendered']));
const context = get(window, '__INITIAL_STATE__.yoho.context');
const {app, router, store} = createApp(context);
const api = createApi();
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__);
... ... @@ -31,9 +32,43 @@ Vue.use(Toast);
Vue.use(DatePicker);
Vue.use(Dialog);
Vue.use(ImagePreview);
Vue.prop('api', createApi());
Vue.prop('api', api);
Vue.use(Lazy, {error: ''});
yoho.auth = async(loginUrl) => {
let user = await sdk.getUser();
if (user && user.uid) {
return user;
} else {
location.href = loginUrl || `${location.origin}/xianyu/passport/login/taobao`;
return;
}
}
yoho.authRealName = async() => {
if (await yoho.auth()) {
let res = await api.get('/api/ufo/sellerOrder/entryStatus');
if (res.code === 200) {
store.commit('SET_USER_SELLER_INFO', res.data);
if (!get(res.data, 'isZhiMaCertWithPhoto')) {
router.push({
name: 'passport.auth'
});
}
}
return res;
}
return {
code: 403,
message: '未登录'
};
}
const fetchAsycData = (matched, r) => {
const asyncDataPromises = matched
.map(({asyncData}) => asyncData && asyncData({store, router: r})).
... ...
... ... @@ -3,8 +3,8 @@ import Common from './common';
import List from './list';
import Product from './product';
import Home from './home';
import Passport from './passport';
import Address from './address';
import Notice from './notice';
export default [...Order, ...Common, ...List, ...Product, ...Home, ...Address, ...Notice];
export default [...Order, ...Common, ...List, ...Product, ...Home, ...Passport, ...Address, ...Notice];
... ...
<template>
<LayoutApp :show-back="true">
<div class="body" ref="body">
<List>
</List>
</div>
<Scroll
ref="scroll"
:scroll-events="['scroll-end','scroll']"
@scroll-end="fetchList"
>
<NoticeItem v-for="(item, index) in noticeList" :key="index" :data="item"></NoticeItem>
</Scroll>
</LayoutApp>
</template>
<script>
import List from './components/list';
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapActions} = createNamespacedHelpers('notice');
import NoticeItem from './components/noticeItem';
import {
Style,
Scroll,
RecycleList
} from 'cube-ui';
export default {
components: {
List
}
NoticeItem,
Style,
Scroll,
RecycleList
},
data(){
return {
data: {},
scrolling: false,
scrollOption: {
pullUpLoad:true,
},
};
},
created(){
this.fetchList();
// this.fetchNoticeList();
},
computed: {
...mapState(['noticeList','fetchNoticePage','isMore']),
},
methods:{
...mapActions(['fetchNoticeList']),
onScroll(){
console.log('onScroll')
this.scrolling = true;
this._scTimer && clearTimeout(this._scTimer);
this._scTimer = setTimeout(() => {
this.scrolling = false;
}, 400);
},
scroll({ y }) {
const height = this.$refs.banner.$el.offsetHeight + this.$refs.header.offsetHeight;
if (-y >= height) {
this.fixed = true;
} else {
this.fixed = false;
}
},
fetchList(){
console.log('fetchList:'+this.isMore)
if (!this.isMore) {
return;
}
console.log('fetchList:'+this.noMore)
this.fetchNoticeList().then(res => {
this.$nextTick(() => {
this.$refs.scroll.forceUpdate(true);
});
});
},
async onPullingUp(){
console.log('onPullingUp:'+this.isMore)
await this.fetchList();
},
},
};
</script>
<style>
.body {
height: 100%;
overflow-y: auto;
}
<style scoped>
.scroll-content {
height: 500px;
}
</style>
\ No newline at end of file
... ...
... ... @@ -8,12 +8,23 @@ export default [
component: () => import(/* webpackChunkName: "order" */ './detail'),
},
{
name: 'OrderConfirm',
path: '/xianyu/order/confirm/:orderCode.html',
component: () => import(/* webpackChunkName: "order" */ './confirm'),
name: 'OrderSellConfirm',
path: '/xianyu/order/sellerconfirm/:orderCode.html',
component: () => import(/* webpackChunkName: "order" */ './seller-confirm'),
props: true
},
{
name: 'OrderBuyConfirm',
path: '/xianyu/order/buyerconfirm/:orderCode.html',
component: () => import(/* webpackChunkName: "order" */ './buyer-confirm'),
props: true
},
{
name: 'PayOk',
path: '/xianyu/order/ok.html',
component: () => import(/* webpackChunkName: "order" */ './pay-ok'),
},
{
name: 'buyerAskOrder', //买家求购确认
path: '/xianyu/order/buyeraskorder.html',
component: () => import('./buyer-ask-order')
... ...
... ... @@ -9,20 +9,21 @@
/>
</div>
<div class="item-info">
<div class="price-status">
<slot name="item-price">
<div>
<div class="price-status">
<span class="price">¥{{ goodsInfo.goodPrice }}</span>
</slot>
<slot name="orderStatus" :orderStatus="order.statuStr"></slot>
<span class="status">
<slot name="orderStatus" :status="order.statuStr"></slot>
</span>
</div>
<p class="item-name">
{{ goodsInfo.productName }}
</p>
</div>
<p class="item-name">
{{ goodsInfo.productName }}
<p class="item-spec">
<span>{{ goodsInfo.colorName }},</span>
<span>{{ goodsInfo.sizeName }}码</span>
</p>
<slot name="item-spec">
<p class="item-spec">
{{ spec }}
</p>
</slot>
</div>
</div>
</template>
... ... @@ -50,7 +51,6 @@ export default {
<style lang="scss" scoped>
.order-item-wrapper {
display: flex;
margin: 40px 0;
.item-img {
min-width: 180px;
... ... @@ -77,10 +77,40 @@ export default {
flex: 1;
flex-direction: column;
margin-left: 20px;
font-size: 24px;
justify-content: space-between;
.price-status {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
.price {
font-size: 28px;
color: #d0021b;
letter-spacing: 0;
font-weight: bold;
}
.status {
color: #000;
letter-spacing: 0;
font-weight: bold;
}
}
.item-name {
color: #999;
letter-spacing: 0;
line-height: 36px;
}
.item-spec {
font-weight: bold;
& > :last-child {
padding-left: 10px;
}
}
}
}
... ...
<template>
<scroll class="" direction="horizontal"></scroll>
<div class="order-status-nav-scroll-wrap">
<scroll ref="statusScroll" :data="statusList" direction="horizontal">
<ul class="list-wrapper">
<li
:class="i === 1 ? 'list-item active' : 'list-item'"
v-for="(statusInfo, i) in statusList"
:key="i"
>
{{ statusInfo.text }}
</li>
</ul>
</scroll>
</div>
</template>
<script>
import { Scroll } from "cube-ui";
import {
buyerOrderStatusList,
sellerOrderStatusList,
ownType
} from "../../../../../constants/order-constants";
export default {
components: {
Scroll
},
computed: {
statusList: function() {
const { owner } = this.$route.params;
return owner === ownType.BUY
? buyerOrderStatusList
: sellerOrderStatusList;
}
}
};
</script>
<style>
.order-status-nav-scroll-wrap .cube-scroll-content {
display: inline-block;
min-width: 100%;
}
</style>
<style lang="scss" scoped>
.order-status-nav-scroll-wrap {
transform: rotate(0deg); // fix 子元素超出边框圆角部分不隐藏的问题
position: relative;
.list-wrapper {
white-space: nowrap;
font-size: 28px;
color: #999;
background: #fff;
box-shadow: inset 0 -1px 0 0 #eee;
display: flex;
justify-content: space-between;
align-items: center;
.list-item {
display: inline-block;
padding: 22px 0;
margin-left: 38px;
&:last-child {
margin-right: 38px;
}
}
.list-item.active {
font-size: 40px;
color: #000;
font-weight: bold;
border-bottom: 6px solid #000;
}
}
}
</style>
\ No newline at end of file
... ...
... ... @@ -4,7 +4,7 @@ const routers = [
// status: 订单状态
{
name: 'BuyerOrderList',
path: '/xianyu/:owner/order/list/:status',
path: '/xianyu/:owner/order/list/:status?',
component: () => import('./order-list'),
},
];
... ...
<template>
<scroll :data="orderList" class="order-list-wrapper">
<order-list-item
v-for="order in orderList"
:key="order.orderCode"
:order="order"
>
<template #orderStatus="{orderStatus}">{{ orderStatus }}</template>
</order-list-item>
</scroll>
<div>
<status-nav />
<scroll :data="orderList" class="order-list-scroll-wrap">
<ul class="list-wrapper">
<li v-for="order in orderList" :key="order.orderCode">
<order-list-item :order="order">
<template #orderStatus="{status}">{{ status }}</template>
</order-list-item>
<div class="actions">
<Button v-for="action in order.buttons" :key="action.code">
{{ action.text }}
</Button>
</div>
</li>
</ul>
</scroll>
</div>
</template>
<script>
import { Button, Scroll } from "cube-ui";
import { createNamespacedHelpers } from "vuex";
import OrderListItem from "./components/order-list-item";
import StatusNav from "./components/status-nav";
const { mapActions, mapState } = createNamespacedHelpers("order/orderList");
... ... @@ -21,7 +30,8 @@ export default {
components: {
Button,
Scroll,
OrderListItem
OrderListItem,
StatusNav
},
computed: {
...mapState(["orderList"])
... ... @@ -45,7 +55,42 @@ export default {
};
</script>
<style lang="scss" scoped>
.order-list-wrapper {
margin: 40px 20px;
.order-list-scroll-wrap {
.list-wrapper {
li {
padding: 40px 40px;
border-bottom: 1px solid #eee;
}
& :last-child {
border-bottom: 0;
}
}
.actions {
display: flex;
justify-content: flex-end;
margin-top: 40px;
button {
font-size: 24px;
padding: 24px 64px 22px 64px;
color: #999;
letter-spacing: 0;
border-radius: 0;
background: #fff;
border: 1px solid #ccc;
line-height: 1.3;
width: 224px;
margin-right: 20px;
}
& :last-child {
background: #002b47;
color: #fff;
border: 1px solid #002b47;
margin-right: 0;
}
}
}
</style>
\ No newline at end of file
... ...
<template>
<LayoutApp :show-back="true">
<div class="body">
<div class="header">
<i class="iconfont iconOk icon-class"></i>
</div>
<div class="title">支付成功</div>
<OrderButton :txt="txt" class="btn-class" @on-click="onClick"></OrderButton>
<div class="info">
<div class="item" @click="goPublish">继续发布</div>
<div class="item" @click="goHome">随便逛逛</div>
</div>
</div>
</LayoutApp>
</template>
<script>
import OrderButton from './components/confirm/button';
export default {
name: 'PayOk',
data() {
return {
txt: '查看商品'
};
},
components: {
OrderButton
},
methods: {
onClick() {
},
goPublish() {
},
goHome() {
}
}
};
</script>
<style lang="scss" scoped>
.body {
height: 100%;
margin: 0 74px;
padding-bottom: 140px;
overflow-y: auto;
}
.header {
margin-top: 170px;
text-align: center;
}
.icon-class {
font-size: 120px;
}
.btn-class {
height: 100px;
font-size: 32px;
line-height: 100px;
}
.title {
font-size: 40px;
font-weight: bold;
text-align: center;
margin-bottom: 60px;
}
.info {
font-size: 28px;
display: flex;
margin-top: 30px;
.item {
width: 50%;
text-align: center;
}
.item + .item {
border-left: 1px solid black;
}
}
</style>
... ...
<template>
<LayoutApp :show-back="true">
<div class="body">
<TitleComp txt="出售"></TitleComp>
<ProductInfo :data="orderDetail.goodsInfo" class="product-info"></ProductInfo>
<InputPrice class="input-price"></InputPrice>
<OrderMargin class="order-item order-margin"></OrderMargin>
<OrderFee class="order-item"></OrderFee>
<AddressInfo :data="orderDetail.userAddress" class="order-item"></AddressInfo>
<OrderAgree v-model="agree" class="order-item"></OrderAgree>
</div>
<ConfirmButton :txt="txt" class="footer"></ConfirmButton>
</LayoutApp>
</template>
<script>
import ProductInfo from './components/confirm/product';
import InputPrice from './components/confirm/input-price';
import AddressInfo from './components/confirm/address';
import ConfirmButton from './components/confirm/button';
import TitleComp from './components/confirm/title';
import OrderMargin from './components/confirm/order-margin';
import OrderFee from './components/confirm/order-fee';
import OrderAgree from './components/confirm/agree';
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapActions, mapMutations } = createNamespacedHelpers('order/orderConfirm');
export default {
name: 'OrderConfirm',
props: {
orderCode: {
type: String,
default: ''
}
},
components: {
ProductInfo,
AddressInfo,
ConfirmButton,
InputPrice,
TitleComp,
OrderMargin,
OrderFee,
OrderAgree
},
data() {
return {
txt: '提交',
agree: false
};
},
mounted() {
this.fetchOrderDetail({orderCode: this.orderCode})
},
computed: {
...mapState(['orderDetail'])
},
methods: {
...mapActions(['fetchOrderDetail'])
}
};
</script>
<style lang="scss" scoped>
.footer {
position: absolute;
bottom: 0;
width: 100%;
z-index: 100;
}
.body {
height: 100%;
margin: 0 40px;
padding-bottom: 140px;
overflow-y: auto;
}
.order-item {
padding-top: 40px;
padding-bottom: 40px;
}
.order-item + .order-item {
border-top: 1px solid #eee;
}
</style>
... ...
<template>
<LayoutApp class="yohoufo-auth-page">
<div class="auth-content">
<div class="auth-title">实名认证</div>
<p class="auth-sub-title">信息仅用于身份验证,不对外展示,有货UFO平台将严保您的信息安全,请认证填写如下信息,确保上传的图片文字清晰可见。</p>
<div class="auth-form">
<p class="form-title">姓名</p>
<div class="form-input-block">
<CubeInput class="auth-input" v-model="name" :disabled="!!certName" placeholder="请输入真实姓名"></CubeInput>
</div>
<p class="form-title">身份证号</p>
<div class="form-input-block">
<CubeInput class="auth-input" v-model="idCode" :disabled="!!certIdCode" placeholder="请输入身份证号"></CubeInput>
</div>
<p class="form-title">上传身份证正面照片 <a :href="exampleLink" class="upload-intro">示例照片</a></p>
<div class="form-input-block">
<div class="upload-btn" @click="uploadFrontCard">
<div v-if="idCardFrontUrl" class="delete-img" @click.stop="clearInfo(['idCardFront', 'idCardFrontUrl'])">ㄨ</div>
<div v-if="idCardFrontUrl" class="up-img-block">
<img :src="idCardFrontUrl">
</div>
</div>
<div class="example-view">
<img src="//img12.static.yhbimg.com/goodsimg/2019/09/25/16/022553bde9fc6bafa0df244de694c551f2.png?imageView/1/w/160/h/100" alt="身份证正面(示例)">
<p>(示例)</p>
</div>
</div>
<p class="form-title">上传身份证反面照片</p>
<div class="form-input-block">
<div class="upload-btn" @click="uploadBackCard">
<div v-if="idCardBackUrl" class="delete-img" @click.stop="clearInfo(['idCardBack', 'idCardBackUrl'])">ㄨ</div>
<div v-if="idCardBackUrl" class="up-img-block">
<img :src="idCardBackUrl">
</div>
</div>
<div class="example-view">
<img src="//img11.static.yhbimg.com/goodsimg/2019/09/25/16/011df7d2637953bd6ce2c59500011d7fd7.png?imageView/1/w/160/h/100" alt="身份证反面(示例)">
<p>(示例)</p>
</div>
</div>
<div class="hide-upload-wrap">
<CubeUpload
ref="upload"
action="/xianyu/upload/idcard"
:simultaneous-uploads="1"
@files-added="addedHandler"
@file-success="successHandler"
@file-error="errorHandler"/>
</div>
</div>
</div>
<div class="auth-footer">
<CubeButton class="submit-btn" :disabled="canSubmit" @click="submitCert">确认提交</CubeButton>
</div>
</LayoutApp>
</template>
<script>
import { get } from 'lodash';
import { Input, Button, Upload } from 'cube-ui';
import { mapActions, mapState } from 'vuex';
export default {
name: 'auth',
data() {
return {
name: '',
idCode: '',
idCardFront: '',
idCardBack: '',
idCardFrontUrl: '',
idCardBackUrl: '',
exampleLink: '//activity.yoho.cn/feature/4899.html?share_id=7593&title=UFO-身份证示例'
}
},
computed: {
...mapState(['yoho']),
certName() {
let name = get(this.yoho, 'user.sellerInfo.certName');
name && (this.name = name);
return name;
},
certIdCode() {
let code = get(this.yoho, 'user.sellerInfo.certNo');
code && (this.idCode = code);
return code;
},
canSubmit() {
return !(this.name && this.idCode && this.idCardFront && this.idCardBack);
}
},
methods: {
...mapActions(['userRealCertification']),
clearInfo(arr) {
if (arr && arr.length) {
arr.forEach(val => {
this[val] && (this[val] = '');
});
}
},
chooseUploadFile() {
this.$refs.upload && this.$refs.upload.$el.querySelector('.cube-upload-input').click();
},
uploadFrontCard() {
if (!this.uploading && !this.idCardFront) {
this.uploadCallback = (path, img) => {
this.idCardFront = path;
this.idCardFrontUrl = img;
};
this.chooseUploadFile();
}
},
uploadBackCard() {
if (!this.uploading && !this.idCardBack) {
this.uploadCallback = (path, img) => {
this.idCardBack = path;
this.idCardBackUrl = img;
};
this.chooseUploadFile();
}
},
addedHandler() {
this.uploading = true;
},
successHandler(file) {
if (get(file, 'response.code') === 200 && this.uploadCallback) {
this.uploadCallback(get(file, 'response.data.imagesList[0]'), file.url);
}
this.uploading = false;
setTimeout(() => {
this.$refs.upload.removeFile(file);
}, 1000);
},
errorHandler() {
this.uploading = false;
},
submitCert() {
this.userRealCertification({
certName: this.name,
certNo: this.idCode,
frontImageUrl: this.idCardFront,
backImageUrl: this.idCardBack
}).then(res => {
if (res.code === 200) {
if (this.$route.query.refer) {
this.$router.push({
name: this.$route.query.refer
});
} else {
this.$router.go(-1);
}
} else {
this.$createToast && this.$createToast({
txt: res.message || '认证失败',
type: 'txt',
time: 1000
}).show();
}
});
}
},
components: {
CubeInput: Input,
CubeButton: Button,
CubeUpload: Upload
}
};
</script>
<style lang="scss" scoped>
.auth-content {
height: calc(100% - 200px);
padding: 0 40px;
overflow-y: scroll;
.auth-title {
font-size: 68px;
line-height: 96px;
font-weight: 900;
}
.auth-sub-title {
font-size: 24px;
color: #999;
line-height: 1.5;
margin-top: 20px;
}
.auth-form {
margin-top: 40px;
}
.form-title {
font-size: 36px;
padding-top: 40px;
line-height: 1.6;
}
.form-input-block {
padding-bottom: 16px;
border-bottom: 1px solid #f6f6f6;
position: relative;
}
.auth-input {
&:after {
border: 0;
}
/deep/ .cube-input-field {
font-size: 14px;
height: 40px;
border: none;
padding: 0;
}
}
.upload-intro {
font-size: 28px;
line-height: 58px;
color: #999;
float: right;
}
.upload-btn {
width: 220px;
height: 140px;
background-color: #eee;
margin: 30px 0 14px;
position: relative;
&:before {
content: "";
width: 40px;
height: 4px;
background-color: #999;
position: absolute;
left: 50%;
top: 50%;
margin-top: -2px;
margin-left: -20px;
}
&:after {
content: "";
width: 4px;
height: 40px;
background-color: #999;
position: absolute;
left: 50%;
top: 50%;
margin-top: -20px;
margin-left: -2px;
}
}
.delete-img {
width: 40px;
height: 40px;
line-height: 40px;
border-radius: 20px;
background-color: #676767;
color: #fff;
text-align: center;
position: absolute;
top: -20px;
right: -20px;
z-index: 2;
}
.up-img-block {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
> img {
display: block;
max-width: 100%;
max-height: 100%;
}
}
.example-view {
width: 160px;
font-size: 20px;
color: #999;
text-align: center;
position: absolute;
right: 0;
top: 16px;
> img {
width: 100%;
}
> p {
font-weight: 300;
}
}
.hide-upload-wrap {
height: 0;
overflow: hidden;
}
}
.auth-footer {
padding: 40px;
.submit-btn {
height: 120px;
background: #022c46;
&.cube-btn_disabled {
background: #ccc;
}
}
}
</style>
... ...
<template>
<div>bind page</div>
</template>
<script>
export default {
name: 'bind',
activated() {
},
beforeRouteUpdate(to, from, next) {
},
mounted() {
this.$yoho.authRealName();
},
methods: {
}
};
</script>
... ...
export default [{
name: 'passport.bind',
path: '/xianyu/passport/bind',
component: () => import(/* webpackChunkName: "passport.bind" */ './bind')
}, {
name: 'passport.auth',
path: '/xianyu/passport/real/auth',
component: () => import(/* webpackChunkName: "passport.auth" */ './auth')
}];
... ...
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1569404435657'); /* IE9 */
src: url('iconfont.eot?t=1569404435657#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAc8AAsAAAAADnAAAAbsAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCFBAqNEIpRATYCJANACyIABCAFhG0HgTQbRwzIDiUFIcHAAAAAQfD8a8d3X5KB88UVK3ZQvWcrAKcX2IKtULl0PbNiu5kPN98LFypiL1XRrylcKEUXQo0JbA6bI19NkBnMYeq1o7WWXzT9h/bIoFYyIVLinujuvjimWr+RCPlDQsSShUioUD1UQqPEjNVwlj0q/45onEmoiFwPAWI6SQMb19DWB1wwcFUwYDmfRwKXig/TYRdCpx851mzB3gUeodrJugdgv3t7+QIGiQsoPA2/0YSF9VxQVbo4HkDl4gYwXavAhvkVA6ONQAMDAZgRfRAtvYUOrQOBxD+/uWEf0IwPkLB0tYfYs+x59iL7Xvs3jiQHx3HN8WDRQeliDybtvdl59EIzYUSIf+EBFBofQ0hCgIcQEVOQkpGD+ACnzPCkrYDSRaVBQenqQf+QYHjwQULgwYBkQWUakgcPCUgRPAQge+HBA/kGaRCoktIQQcVJQwzVtTQUUN1LQwrVfQ8ZyAOMIgfAmmO6AugGkNcAdRFwutwVeyiB5YwsF4wpUehI1zSVKMtzu097inep83f1SHtaJw+bVVvr68P3gaTZPGCxDJpM/UbjjFT6PFNe32zuc1u7V7NbuUu9Z5iKMbjxxw0UJVdgWnXQgL5LYL9OKcRCcfCgIaU5oE+rVii6p6qIO+iupBen8IDH9GptAMvjiFapC/RkHdVpDMHQ87hBpSfN9gGLY9D0Tb/x61F3LJ72wE1t7HPf+rB+5/HA3WDDFWPaqk0gObUxeK3FgqlE6gQqaP5m3s4N1wdwkU55SqGJR4ELr+ZuU59pfSfUGtVZzurdmr2qPepd/Rt26FL9Hgw7fzV178HgDU/UjyI2kWC9uq+7lLTrXAj4v7WPXo9hGw3BvjodkA1aEcXr5yJyj31umq8Rz9pv+iZ+p2PQYsZ9y8B9yztHv+a+yfjIaL4StdUy7XXDHMN212/0vm6KxjcZZ7adC8FXn4/33nkxnL3mQgJnH3XbGdjPM2zfogha1LsQMBt+UuF1zUjgm0313jctsewt5iGudvL1FFMzi8PP4bOz+DmBDKOlYoj708LN0GKBkDczBlNY6+4suCdJdMk46+WbEwq3m/Up6eoAHxJ+nP3NGndnqPk/3OgJtGwN3X5MOBN3f5ZqQEm3j8q1d+KssxE3nu3K7sed6YvslUDRL71i2nmv317n6WnpGb8z0mlFxapQDAXuhY9woiv+kUCG0VLxV4CL47s6jv+9YqhXsNKpu/t1jc+7mxZ+yQoVrujxV72c2OnBnn4KDcQ1JLeAa2XB3q+r/UCa2/6Sd4ZLHbkM8RYHeoO6wzVRa5FuoX4tfiXBt1RN3tquV8/jc3P4eYEMo6ViiPtXpVvghx9CyFsYgyns4MhHvp4Fvu107TfeLhN7+lw891QXT2zs8FAt2bDhkWFw1+6+UFiQ7bsd5lBtm35yB8m3TW5j8GJ6fnZ+vqbm9JnTpzZ8nXfHPwg5Bw9+991BJsMhAqqgxnmwrTvqwLODV5C5z4G6+rUp3W1tCjk3GFd1ryBj69S928MSibBurMegwI3tPVj3vRoSTkbUezb0NXjWRSjYrHF1oOAn3xZdV+dn89OuaNop21+/KdfN5pZ7weYeAp6ZPSZPnpgKBit3bN0M4dN/pGdCQyY/qLRmpyPS1qq1UxY4lBtuC889ZI047M8imraKw0ImhfPULG1seKs7WKmVWTiQ38Pq/wBwl+FFWAkAfZOV6JI5K9wKY4teYS3AWvclyJqqAABsgK0BuFegn1ZlMUq4ESrjDPA7yDfY2SrBbs3bNK/wSpnlXfbPxf09Y83bw4P+rMYpncB8+hCiIJl/OSgLInlGiL8i91vzrdLJKrE6b1B//mOw1MOGx5OHWFSCo8pabMukDcn8axBtKEAR0AGakJ4aYxgIHglDwCdkJMQMoPHkhDpoIGIioD+vZCBYDoIi5z5oLPc1xmAFj0Z+Ah8rEGImiv85E3oL362WRiSDuMToNMHnSMSUDDJufmAP4smFJF0WnWMA0TJ+A5GdkVXvaERiRM/jANkEL4dhKIKiJSKiIbsZEgolhJSWjCMOkzHGMNKSzEwqdmAGRyICboyFBkHCmORcCEaZzs2HgwQx1OoMbo3X7wHBQ44QErpjxp/cAyBoZPCjJ8gmg6wETSMWJ824LsNlTMAjB4aKKQg1nkaCCIIGKkYIRasEAml8q3EQHBgyGJuiWUoJmbQSlYozlk9Fpzzm5JuVP4sVlmgx4okvgYQSSSyJpJJJLoVY8QVfcjQuxpJL2VzJpFjM5qeTXK7LM3IkY47XrrgQUQyb5vPGGNfmOtluUqFcli7ii003fJTkCJyTh9BIJvPkjKFK7hVcRJFyIcMmbmxTSi4UskmalkwCAAAAAA==') format('woff2'),
url('iconfont.woff?t=1569404435657') format('woff'),
url('iconfont.ttf?t=1569404435657') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1569404435657#iconfont') format('svg'); /* iOS 4.1- */
src: url('iconfont.eot?t=1569468612498'); /* IE9 */
src: url('iconfont.eot?t=1569468612498#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('iconfont.woff?t=1569468612498') format('woff'),
url('iconfont.ttf?t=1569468612498') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1569468612498#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
... ... @@ -71,6 +71,10 @@
content: "\e7ce";
}
.iconOk:before {
content: "\e662";
}
.iconarrow:before {
content: "\e7c4";
}
... ...
... ... @@ -62,6 +62,9 @@ Created by iconfont
<glyph glyph-name="check_full" unicode="&#59342;" d="M878.545455 872.727273h-733.09091A122.298182 122.298182 0 0 1 23.272727 750.545455v-733.09091A122.298182 122.298182 0 0 1 145.454545-104.727273h733.09091A122.298182 122.298182 0 0 1 1000.727273 17.454545v733.09091A122.298182 122.298182 0 0 1 878.545455 872.727273z m-112.872728-369.105455l-282.181818-280.436363a29.090909 29.090909 0 0 0-40.96 0L258.327273 406.109091a29.090909 29.090909 0 0 0 40.96 41.309091l163.607272-162.909091 261.701819 260.305454a29.090909 29.090909 0 1 0 40.96-41.30909z" horiz-adv-x="1024" />
<glyph glyph-name="Ok" unicode="&#58978;" d="M512-128a512 512 0 1 0 512 512 512.576 512.576 0 0 0-512-512z m0 960a448 448 0 1 1 448-448A448.512 448.512 0 0 1 512 832z m-34.848-630.4a32 32 0 0 0-22.688-9.6H454.4a32 32 0 0 0-22.624 9.376l-166.4 166.4A32 32 0 0 0 310.624 412.8l143.424-143.36 259.2 264.864a32 32 0 1 0 45.76-44.8z" horiz-adv-x="1024" />
<glyph glyph-name="arrow" unicode="&#59332;" d="M1536 896L768-128 0 896z" horiz-adv-x="1536" />
... ...
... ... @@ -3,21 +3,18 @@ import { get } from 'lodash';
export default {
async fetchNoticeList({ commit, state }) {
console.log("==="+state.fetchNoticeList)
if (state.fetchNoticeList) {
return {};
}
let page = state.fetchNoticePage || 1;
console.log("page:"+page)
commit(Types.FETCH_NOTICE_LIST_REQUEST, { page });
const result = await this.$api.post('/api/ufo/home/noticelist', {
page,
size: 10,
size: 8,
lastedTime: state.fetchNoticeLastedTime || void 0
});
console.log(result)
if (result && result.code === 200) {
commit(Types.FETCH_NOTICE_LIST_SUCCESS, {
data: result.data,
... ...
... ... @@ -8,6 +8,7 @@ export default function() {
noticeList:[],
fetchNoticePage: 1,
fetchNoticeLastedTime: 0,
isMore: true,
},
actions,
mutations,
... ...
... ... @@ -2,7 +2,7 @@ import * as Types from './types';
export default {
[Types.FETCH_NOTICE_LIST_REQUEST](state, { page }) {
console.log("page:"+page+"==="+state.fetchNoticeList)
state.fetchNoticeList = false;
if (page === 1) {
state.fetchNoticeLastedTime = 0;
... ... @@ -11,10 +11,17 @@ export default {
},
[Types.FETCH_NOTICE_LIST_SUCCESS](state, { data }) {
state.fetchNoticeList = false;
state.fetchNoticePage += 1;
state.fetchNoticePage = data.page +1;
state.fetchNoticeLastedTime = data.lastedTime;
if(data.page < data.totalPage){
state.isMore = true;
}else {
state.isMore = false;
}
const noticeList = data.list || [];
if (noticeList.length) {
noticeList.forEach(val => {
state.noticeList.push(val);
... ...
... ... @@ -117,6 +117,9 @@ export default function(mergeState = {}) {
[Types.FETCH_USER_INFO_FAILD](state) {
state.context.userHeadIco = '';
},
[Types.SET_USER_SELLER_INFO](state, sellerInfo) {
state.user.sellerInfo = sellerInfo || {};
}
},
getters: {
getLogin(state) {
... ... @@ -138,6 +141,14 @@ export default function(mergeState = {}) {
return {};
}
},
userRealCertification({ commit }, { certName, certNo, frontImageUrl, backImageUrl }) {
return this.$api.post('/api/ufo/user/certification', {
certName,
certNo,
frontImageUrl,
backImageUrl
});
},
reportError(params, {error}) {
this.$reportError(error);
},
... ...
... ... @@ -2,6 +2,7 @@ export const SET_SERVER_INFO = 'SET_SERVER_INFO';
export const SET_ENV = 'SET_ENV';
export const SET_TITLE = 'SET_TITLE';
export const SET_USER = 'SET_USER';
export const SET_USER_SELLER_INFO = 'SET_USER_SELLER_INFO';
export const ROUTE_CHANGE = 'ROUTE_CHANGE';
export const REPORT_YAS = 'REPORT_YAS';
export const SET_NEED_LOGIN = 'SET_NEED_LOGIN';
... ...
... ... @@ -2,6 +2,7 @@ const orderApi = require('./order-api-map');
const productApi = require('./product-api-map');
const listApi = require('./list-api-map');
const homeApi = require('./home-api-map');
const passportApi = require('./passport-api-map');
const addressApi = require('./address-api-map');
const orderListApi = require('./order-list-api-map');
... ... @@ -10,6 +11,7 @@ module.exports = {
...productApi,
...listApi,
...homeApi,
...passportApi,
...addressApi,
...orderListApi,
};
... ...
... ... @@ -2,45 +2,45 @@ module.exports = {
'/api/ufo/mine/rollBoardList': {
ufo: true,
api: 'ufo.users.rollNoticeList',
params: {}
params: {},
},
'/api/ufo/mine/seller/orderSummary': {
ufo: true,
api: 'ufo.seller.orderSummary',
params: {
uid: {type: Number}
}
uid: { type: Number },
},
},
'/api/ufo/mine/favoriteNum': {
ufo: true,
api: 'ufo.user.favoriteNum',
params: {
uid: {type: Number}
}
uid: { type: Number },
},
},
'/api/ufo/mine/depositNum': {
ufo: true,
api: 'ufo.deposit.queryUserStorageCount',
params: {
uid: {type: Number}
}
uid: { type: Number },
},
},
'/api/ufo/mine/assets': {
ufo: true,
api: 'ufo.asssets.details',
params: {
uid: {type: Number},
limit: {type: Number},
page: {type: Number}
}
uid: { type: Number },
limit: { type: Number },
page: { type: Number },
},
},
'/api/ufo/mine/order/summary': {
ufo: true,
path: '/shopping',
api: 'ufo.order.summary',
params: {
uid: {type: Number}
}
uid: { type: Number },
},
},
'/api/ufo/mine/resource': {
ufo: true,
... ... @@ -48,17 +48,17 @@ module.exports = {
params: {
content_code: { type: String },
uid: { type: Number },
}
},
},
'/api/ufo/home/noticelist':{
'/api/ufo/home/noticelist': {
ufo: true,
api: 'ufo.users.noticeList',
params:{}
params: {},
},
'/api/ufo/home/favoriteProduct':{
'/api/ufo/home/favoriteProduct': {
ufo: true,
api: 'ufo.user.favoriteList',
params:{}
params: {},
},
'/api/ufo/channel/channelList': {
ufo: true,
... ... @@ -66,6 +66,6 @@ module.exports = {
params: {
content_code: { type: String },
uid: { type: Number, require: true },
}
},
},
};
... ...
module.exports = {
'/api/ufo/sellerOrder/entryStatus': {
ufo: true,
auth: true,
api: 'ufo.sellerOrder.entryStatus',
params: {}
},
'/api/ufo/user/certification': {
ufo: true,
auth: true,
api: 'ufo.user.alipayCertification',
params: {
certName: { type: String, require: true },
certNo: { type: String, require: true },
frontImageUrl: { type: String, require: true },
backImageUrl: { type: String, require: true }
}
},
};
... ...
... ... @@ -21,3 +21,47 @@ export const buyerOrderStatus = {
DONE: 5, // 交易成功
FAILED: 6, // 交易失败
};
// 卖家订单状态, 接口对应查询参数: type
export const sellerOrderStatusList = [
{
value: 1,
text: '出售中',
},
{
value: 2,
text: '待发货',
},
{
value: 3,
text: '已发货',
},
{
value: 5,
text: '交易完成',
},
{
value: 6,
text: '交易失败',
},
];
// 买家订单状态,接口对应查询参数: type
export const buyerOrderStatusList = [
{
value: 1,
text: '全部',
},
{
value: 2,
text: '待付款',
},
{
value: 3,
text: '待发货',
},
{
value: 4,
text: '待收货',
},
];
... ...
... ... @@ -88,6 +88,33 @@ exports.createApp = async(app) => {
app.use(userMiddleware);
app.use(serverMiddleware);
if (!app.locals.proEnv) {
app.use((req, res, next) => {
if (/cordova/.test(req.url)) {
return res.status(404).end();
}
return next();
});
app.use('/favicon.ico', (req, res) => {
res.send('');
});
app.use('/passport/login/user', (req, res) => {
let result = {
code: 403,
message: '未登录',
data: ''
};
if (req.user.uid) {
result.code = 200;
result.message = '已登录';
result.data = req.user.uid.toString();
console.log(req.user);
}
res.jsonp(result);
});
}
app.use('/xianyu', ssrApiMiddleware);
app.use(ssrRouteMiddleware.routers);
... ...
const express = require('express');
const multipart = require('connect-multiparty');
const passport = require('../models/passport');
const upload = require('../models/upload');
const app = express();
const router = express.Router(); // eslint-disable-line
const multipartMiddleware = multipart();
router.get('/xianyu/passport/login/taobao', passport.login.taobaoLogin)
router.get('/xianyu/passport/callback/taobao', passport.login.taobaoCallback)
router.post('/xianyu/upload/idcard', multipartMiddleware, upload.uploadIdCard);
router.get('/xianyu/passport/login/taobao', passport.login.taobaoLogin);
router.get('/xianyu/passport/callback/taobao', passport.login.taobaoCallback);
app.use(router);
... ...
... ... @@ -67,66 +67,12 @@ util.inherits(Strategy, OAuth2Strategy);
* @api protected
*/
Strategy.prototype.userProfile = function (accessToken, done) {
console.log(client)
client.execute('taobao.user.buyer.get', {
session: accessToken,
fields: 'nick,sex'
client.execute('taobao.openuid.get', {
session: accessToken
}).then(res => {
console.log(res);
});
return;
console.log(arguments)
let oauth2 = this._oauth2;
let url = 'https://eco.taobao.com/router/rest';
let params = {
method: 'taobao.user.buyer.get',
app_key: oauth2._clientId,
session: accessToken,
format: 'json',
v: '2.0',
fields: 'uid,nick,avatar'
};
// _clientSecret
// ?format=json&v=2.0&fields=uid,nick,avatar&method=taobao.user.buyer.get';
// url = url + '&app_key=' + oauth2._clientId;
// url = url + '&timestamp=' + Date.parse(new Date());
// url = url + '&access_token=' + accessToken;
if (!accessToken) {
return done(new Error('accessToken is empty'));
}
Object.keys(signMD5(params, oauth2._clientSecret)).forEach((k, i) => {
url += `${i ? '&' : '?'}${k}=${params[k]}`;
});
console.log(url)
oauth2.get(url, accessToken, function (err, result, res) {
if (err) {
return done(new InternalOAuthError('failed to fetch user profile', err));
}
try {
if (result) {
console.log(result)
let json = JSON.parse(result);
if (json.error_response)
return done(new InternalOAuthError(json.error_response.code + '-' + json.error_response.msg, new Error(json.error_response.msg)));
else {
let json = JSON.parse(result);
let profile = { provider: 'taobao' };
profile.id = json.uid;
profile.nickname = json.nick;
profile.avatar = json.avatar;
profile._raw = result;
profile._json = json;
done(null, profile);
}
}
} catch (e) {
done('ERROR:' + e + result);
}
done(null, res);
}).catch(e => {
done(new InternalOAuthError('failed to fetch open uid', e));
});
}
... ...
... ... @@ -4,7 +4,9 @@ const TaobaoStrategy = require('./passport-taobao');
const uuid = require('uuid');
const log = global.yoho.logger;
const config = global.yoho.config;
const loginPage = `${config.siteUrl}/signin.html`;
// taobao 登录
passport.use('taobao', new TaobaoStrategy({
... ... @@ -26,17 +28,14 @@ const login = {
})(req, res, next);
},
taobaoCallback: (req, res, next) => {
console.log(req.session)
// return res.send(req.query);
passport.authenticate('taobao', (err, user) => {
console.log('21312312')
console.log(user)
console.log(err)
if (err || !user) {
log.error(`taobao authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
res.json(user);
})(req, res, next);
// if (req.session.authState === req.query.state) {
// } else {
// res.send('error');
// }
}
}
... ...
const fs = require('fs');
const _ = require('lodash');
const request = require('request-promise');
const log = global.yoho.logger;
const upload = ({ files, type, size, bucket }) => {
let reqFiles = [];
let errTip = '';
if (!_.isArray(files)) {
files = [files];
}
size = size || 10 * 1024 * 1024;
files.forEach(file => {
let fileType = file.type.split('/');
if (!fileType || (type && fileType[0] !== type)) {
errTip = '上传文件格式不正确!';
}
if (file.size > size) {
errTip = '上传文件尺寸太大!';
}
reqFiles.push(fs.createReadStream(file.path));
reqFiles.push(file.name);
});
if (errTip) {
return Promise.resolve({
code: 403,
message: errTip
});
}
return request({
method: 'post',
url: 'http://upload.static.yohobuy.com',
formData: {
fileData: reqFiles,
project: bucket
},
json: true
}).catch(err => {
let message = 'file upload error';
log.error(message, JSON.stringify(err));
return {
code: 403,
message
};
});
}
const uploadIdCard = (req, res, next) => {
upload({
files: req.files && req.files.file || [],
type: 'image',
bucket: 'yohocard'
}).then(res.json).catch(next);
}
module.exports = {
uploadIdCard
};
... ...
declare module 'yohoApi' {
type PlainObject<T = any> = { [key: string]: T };
type ApiMap = { [key: string]: UrlParam };
interface UrlParam {
api: string; // 接口方法名
ufo: boolean; // 是否是ufo接口
service: boolean; // service接口
path: string; // 接口路由
auth?: boolean; // 接口是否需要登陆,ture时请求参数会自动添加uid
params?: PlainObject; // 仅用于参数校验,不会合并到请求参数中
}
}
... ...
declare module 'yoho' {
interface OrderOwner {}
}
... ...