From 68c5ef7d9fea3f911e250fb5f8b300bc76099e49 Mon Sep 17 00:00:00 2001
From: jiangping <jp@doumee.com>
Date: 星期四, 26 十月 2023 13:55:49 +0800
Subject: [PATCH] 小程序
---
minipro_standard/uni_modules/uview-ui/components/u-upload/u-upload.vue | 558 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 558 insertions(+), 0 deletions(-)
diff --git a/minipro_standard/uni_modules/uview-ui/components/u-upload/u-upload.vue b/minipro_standard/uni_modules/uview-ui/components/u-upload/u-upload.vue
new file mode 100644
index 0000000..1dac8a7
--- /dev/null
+++ b/minipro_standard/uni_modules/uview-ui/components/u-upload/u-upload.vue
@@ -0,0 +1,558 @@
+<template>
+ <view class="u-upload" :style="[$u.addStyle(customStyle)]">
+ <view class="u-upload__wrap" >
+ <template v-if="previewImage">
+ <view
+ class="u-upload__wrap__preview"
+ v-for="(item, index) in lists"
+ :key="index"
+ >
+ <image
+ v-if="item.isImage || (item.type && item.type === 'image')"
+ :src="item.thumb || item.url"
+ :mode="imageMode"
+ class="u-upload__wrap__preview__image"
+ @tap="onPreviewImage(item)"
+ :style="[{
+ width: $u.addUnit(width),
+ height: $u.addUnit(height)
+ }]"
+ />
+ <view
+ v-else
+ class="u-upload__wrap__preview__other"
+ >
+ <u-icon
+ color="#80CBF9"
+ size="26"
+ :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'folder'"
+ ></u-icon>
+ <text class="u-upload__wrap__preview__other__text">{{item.isVideo || (item.type && item.type === 'video') ? '瑙嗛' : '鏂囦欢'}}</text>
+ </view>
+ <view
+ class="u-upload__status"
+ v-if="item.status === 'uploading' || item.status === 'failed'"
+ >
+ <view class="u-upload__status__icon">
+ <u-icon
+ v-if="item.status === 'failed'"
+ name="close-circle"
+ color="#ffffff"
+ size="25"
+ />
+ <u-loading-icon
+ size="22"
+ mode="circle"
+ color="#ffffff"
+ v-else
+ />
+ </view>
+ <text
+ v-if="item.message"
+ class="u-upload__status__message"
+ >{{ item.message }}</text>
+ </view>
+ <view
+ class="u-upload__deletable"
+ v-if="item.status !== 'uploading' && (deletable || item.deletable)"
+ @tap.stop="deleteItem(index)"
+ >
+ <view class="u-upload__deletable__icon">
+ <u-icon
+ name="close"
+ color="#ffffff"
+ size="10"
+ ></u-icon>
+ </view>
+ </view>
+ <view
+ class="u-upload__success"
+ v-if="item.status === 'success'"
+ >
+ <!-- #ifdef APP-NVUE -->
+ <image
+ :src="successIcon"
+ class="u-upload__success__icon"
+ ></image>
+ <!-- #endif -->
+ <!-- #ifndef APP-NVUE -->
+ <view class="u-upload__success__icon">
+ <u-icon
+ name="checkmark"
+ color="#ffffff"
+ size="12"
+ ></u-icon>
+ </view>
+ <!-- #endif -->
+ </view>
+ </view>
+
+ </template>
+
+ <template v-if="isInCount">
+ <view
+ v-if="$slots.default || $slots.$default"
+ @tap="chooseFile"
+ >
+ <slot />
+ </view>
+ <view
+ v-else
+ class="u-upload__button"
+ :hover-class="!disabled ? 'u-upload__button--hover' : ''"
+ hover-stay-time="150"
+ @tap="chooseFile"
+ :class="[disabled && 'u-upload__button--disabled']"
+ :style="[{
+ width: $u.addUnit(width),
+ height: $u.addUnit(height)
+ }]"
+ >
+ <u-icon
+ :name="uploadIcon"
+ size="26"
+ :color="uploadIconColor"
+ ></u-icon>
+ <text
+ v-if="uploadText"
+ class="u-upload__button__text"
+ >{{ uploadText }}</text>
+ </view>
+ </template>
+ </view>
+
+ </view>
+</template>
+
+<script>
+ import {
+ chooseFile
+ } from './utils';
+ import mixin from './mixin.js';
+ import props from './props.js';
+
+ /**
+ * upload 涓婁紶
+ * @description 璇ョ粍浠剁敤浜庝笂浼犲浘鐗囧満鏅�
+ * @tutorial https://uviewui.com/components/upload.html
+ * @property {String} accept 鎺ュ彈鐨勬枃浠剁被鍨�, 鍙�夊�间负all media image file video 锛堥粯璁� 'image' 锛�
+ * @property {String | Array} capture 鍥剧墖鎴栬棰戞嬀鍙栨ā寮忥紝褰揳ccept涓篿mage绫诲瀷鏃惰缃甤apture鍙�夐澶朿amera鍙互鐩存帴璋冭捣鎽勫儚澶达紙榛樿 ['album', 'camera'] 锛�
+ * @property {Boolean} compressed 褰揳ccept涓簐ideo鏃剁敓鏁堬紝鏄惁鍘嬬缉瑙嗛锛岄粯璁や负true锛堥粯璁� true 锛�
+ * @property {String} camera 褰揳ccept涓簐ideo鏃剁敓鏁堬紝鍙�夊�间负back鎴杅ront锛堥粯璁� 'back' 锛�
+ * @property {Number} maxDuration 褰揳ccept涓簐ideo鏃剁敓鏁堬紝鎷嶆憚瑙嗛鏈�闀挎媿鎽勬椂闂达紝鍗曚綅绉掞紙榛樿 60 锛�
+ * @property {String} uploadIcon 涓婁紶鍖哄煙鐨勫浘鏍囷紝鍙兘鍐呯疆鍥炬爣锛堥粯璁� 'camera-fill' 锛�
+ * @property {String} uploadIconColor 涓婁紶鍖哄煙鐨勫浘鏍囩殑瀛椾綋棰滆壊锛屽彧鑳藉唴缃浘鏍囷紙榛樿 #D3D4D6 锛�
+ * @property {Boolean} useBeforeRead 鏄惁寮�鍚枃浠惰鍙栧墠浜嬩欢锛堥粯璁� false 锛�
+ * @property {Boolean} previewFullImage 鏄惁鏄剧ず缁勪欢鑷甫鐨勫浘鐗囬瑙堝姛鑳斤紙榛樿 true 锛�
+ * @property {String | Number} maxCount 鏈�澶т笂浼犳暟閲忥紙榛樿 52 锛�
+ * @property {Boolean} disabled 鏄惁鍚敤锛堥粯璁� false 锛�
+ * @property {String} imageMode 棰勮涓婁紶鐨勫浘鐗囨椂鐨勮鍓ā寮忥紝鍜宨mage缁勪欢mode灞炴�т竴鑷达紙榛樿 'aspectFill' 锛�
+ * @property {String} name 鏍囪瘑绗︼紝鍙互鍦ㄥ洖璋冨嚱鏁扮殑绗簩椤瑰弬鏁颁腑鑾峰彇
+ * @property {Array} sizeType 鎵�閫夌殑鍥剧墖鐨勫昂瀵�, 鍙�夊�间负original compressed锛堥粯璁� ['original', 'compressed'] 锛�
+ * @property {Boolean} multiple 鏄惁寮�鍚浘鐗囧閫夛紝閮ㄥ垎瀹夊崜鏈哄瀷涓嶆敮鎸� 锛堥粯璁� false 锛�
+ * @property {Boolean} deletable 鏄惁灞曠ず鍒犻櫎鎸夐挳锛堥粯璁� true 锛�
+ * @property {String | Number} maxSize 鏂囦欢澶у皬闄愬埗锛屽崟浣嶄负byte 锛堥粯璁� Number.MAX_VALUE 锛�
+ * @property {Array} fileList 鏄剧ず宸蹭笂浼犵殑鏂囦欢鍒楄〃
+ * @property {String} uploadText 涓婁紶鍖哄煙鐨勬彁绀烘枃瀛�
+ * @property {String | Number} width 鍐呴儴棰勮鍥剧墖鍖哄煙鍜岄�夋嫨鍥剧墖鎸夐挳鐨勫尯鍩熷搴︼紙榛樿 80 锛�
+ * @property {String | Number} height 鍐呴儴棰勮鍥剧墖鍖哄煙鍜岄�夋嫨鍥剧墖鎸夐挳鐨勫尯鍩熼珮搴︼紙榛樿 80 锛�
+ * @property {Object} customStyle 缁勪欢鐨勬牱寮忥紝瀵硅薄褰㈠紡
+ * @event {Function} afterRead 璇诲彇鍚庣殑澶勭悊鍑芥暟
+ * @event {Function} beforeRead 璇诲彇鍓嶇殑澶勭悊鍑芥暟
+ * @event {Function} oversize 鏂囦欢瓒呭嚭澶у皬闄愬埗
+ * @event {Function} clickPreview 鐐瑰嚮棰勮鍥剧墖
+ * @event {Function} delete 鍒犻櫎鍥剧墖
+ * @example <u-upload :action="action" :fileList="fileList" ></u-upload>
+ */
+ export default {
+ name: "u-upload",
+ mixins: [uni.$u.mpMixin, uni.$u.mixin, mixin,props],
+ data() {
+ return {
+ // #ifdef APP-NVUE
+ successIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKKADAAQAAAABAAAAKAAAAAB65masAAACP0lEQVRYCc3YXygsURwH8K/dpcWyG3LF5u/6/+dKVylSypuUl6uUPMifKMWL8oKEB1EUT1KeUPdR3uTNUsSLxb2udG/cbvInNuvf2rVnazZ/ZndmZ87snjM1Z+Z3zpzfp9+Z5mEAhlvjRtZgCKs+gnPAOcAkkMOR4jEHfItjDvgRxxSQD8cM0BuOCaAvXNCBQrigAsXgggYUiwsK0B9cwIH+4gIKlIILGFAqLiBAOTjFgXJxigJp4BQD0sIpAqSJow6kjSNAFTnRaHJwLenD6Mud52VQAcrBfTd2oyq+HtGaGGWAcnAVcXWoM3bCZrdi+ncPfaAcXE5UKVpdW/vitGPqqAtn98d0gXJwX7Qp6MmegUYVhvmTIezdmHlxJCjpHRTCFerLkRRu4k0aqdajN3sWOo0BK//msHa+xDuPC/oNFMKRhTtM4xjIX0SCNpXL4+7VIaHuyiWEp2L7ahWLf8fejfPdqPmC3mJicORZUp1CQzm+GiphvljGk+PBvWRbxii+xVTj5M6CiZ/tsDufvaXyxEUDxeLIyvu3m0iOyEFWVAkydcVYdyFrE9tQk9iMq6f/GNlvwt3LjQfh60LUrw9/cFyyMJUW/XkLSNMV4Mi6C5ML+ui4x5ClAX9sB9w0wV6wglJwJCv5fOxcr6EstgbGiEw4XcfUry4cWrcEUW8n+ARKxXEJHhw2WG43UKSvwI/TSZgvl7kh0b3XLZaLEy0QmMgLZAVH7J+ALOE+AVnDvQOyiPMAWcW5gSzjCPAV+78S5WE0GrQAAAAASUVORK5CYII=',
+ // #endif
+ lists: [],
+ isInCount: true,
+ }
+ },
+ watch: {
+ // 鐩戝惉鏂囦欢鍒楄〃鐨勫彉鍖栵紝閲嶆柊鏁寸悊鍐呴儴鏁版嵁
+ fileList: {
+ immediate: true,
+ handler() {
+ this.formatFileList()
+ }
+ },
+ },
+ methods: {
+ formatFileList() {
+ const {
+ fileList = [], maxCount
+ } = this;
+ const lists = fileList.map((item) =>
+ Object.assign(Object.assign({}, item), {
+ // 濡傛灉item.url涓烘湰鍦伴�夋嫨鐨刡lob鏂囦欢鐨勮瘽锛屾棤娉曞垽鏂叾涓簐ideo杩樻槸image锛屾澶勪紭鍏堥�氳繃accept鍋氬垽鏂鐞�
+ isImage: this.accept === 'image' || uni.$u.test.image(item.url || item.thumb),
+ isVideo: this.accept === 'video' || uni.$u.test.video(item.url || item.thumb),
+ deletable: typeof(item.deletable) === 'boolean' ? item.deletable : this.deletable,
+ })
+ );
+ this.lists = lists
+ this.isInCount = lists.length < maxCount
+ },
+ chooseFile() {
+ const {
+ maxCount,
+ multiple,
+ lists,
+ disabled
+ } = this;
+ if (disabled) return;
+ // 濡傛灉鐢ㄦ埛浼犲叆鐨勬槸瀛楃涓诧紝闇�瑕佹牸寮忓寲鎴愭暟缁�
+ let capture;
+ try {
+ capture = uni.$u.test.array(this.capture) ? this.capture : this.capture.split(',');
+ }catch(e) {
+ capture = [];
+ }
+ chooseFile(
+ Object.assign({
+ accept: this.accept,
+ multiple: this.multiple,
+ capture: capture,
+ compressed: this.compressed,
+ maxDuration: this.maxDuration,
+ sizeType: this.sizeType,
+ camera: this.camera,
+ }, {
+ maxCount: maxCount - lists.length,
+ })
+ )
+ .then((res) => {
+ this.onBeforeRead(multiple ? res : res[0]);
+ })
+ .catch((error) => {
+ this.$emit('error', error);
+ });
+ },
+ // 鏂囦欢璇诲彇涔嬪墠
+ onBeforeRead(file) {
+ const {
+ beforeRead,
+ useBeforeRead,
+ } = this;
+ let res = true
+ // beforeRead鏄惁涓轰竴涓柟娉�
+ if (uni.$u.test.func(beforeRead)) {
+ // 濡傛灉鐢ㄦ埛瀹氫箟浜嗘鏂规硶锛屽垯鍘绘墽琛屾鏂规硶锛屽苟浼犲叆璇诲彇鐨勬枃浠跺洖璋�
+ res = beforeRead(file, this.getDetail());
+ }
+ if (useBeforeRead) {
+ res = new Promise((resolve, reject) => {
+ this.$emit(
+ 'beforeRead',
+ Object.assign(Object.assign({
+ file
+ }, this.getDetail()), {
+ callback: (ok) => {
+ ok ? resolve() : reject();
+ },
+ })
+ );
+ });
+ }
+ if (!res) {
+ return;
+ }
+ if (uni.$u.test.promise(res)) {
+ res.then((data) => this.onAfterRead(data || file));
+ } else {
+ this.onAfterRead(file);
+ }
+ },
+ getDetail(index) {
+ return {
+ name: this.name,
+ index: index == null ? this.fileList.length : index,
+ };
+ },
+ onAfterRead(file) {
+ const {
+ maxSize,
+ afterRead
+ } = this;
+ const oversize = Array.isArray(file) ?
+ file.some((item) => item.size > maxSize) :
+ file.size > maxSize;
+ if (oversize) {
+ this.$emit('oversize', Object.assign({
+ file
+ }, this.getDetail()));
+ return;
+ }
+ if (typeof afterRead === 'function') {
+ afterRead(file, this.getDetail());
+ }
+ this.$emit('afterRead', Object.assign({
+ file
+ }, this.getDetail()));
+ },
+ deleteItem(index) {
+ this.$emit(
+ 'delete',
+ Object.assign(Object.assign({}, this.getDetail(index)), {
+ file: this.fileList[index],
+ })
+ );
+ },
+ // 棰勮鍥剧墖
+ onPreviewImage(item) {
+ if (!item.isImage || !this.previewFullImage) return
+ uni.previewImage({
+ // 鍏坒ilter鎵惧嚭涓哄浘鐗囩殑item锛屽啀杩斿洖filter缁撴灉涓殑鍥剧墖url
+ urls: this.lists.filter((item) => this.accept === 'image' || uni.$u.test.image(item.url || item.thumb)).map((item) => item.url || item.thumb),
+ current: item.url || item.thumb,
+ fail() {
+ uni.$u.toast('棰勮鍥剧墖澶辫触')
+ },
+ });
+ },
+ onPreviewVideo(event) {
+ if (!this.data.previewFullImage) return;
+ const {
+ index
+ } = event.currentTarget.dataset;
+ const {
+ lists
+ } = this.data;
+ wx.previewMedia({
+ sources: lists
+ .filter((item) => isVideoFile(item))
+ .map((item) =>
+ Object.assign(Object.assign({}, item), {
+ type: 'video'
+ })
+ ),
+ current: index,
+ fail() {
+ uni.$u.toast('棰勮瑙嗛澶辫触')
+ },
+ });
+ },
+ onClickPreview(event) {
+ const {
+ index
+ } = event.currentTarget.dataset;
+ const item = this.data.lists[index];
+ this.$emit(
+ 'clickPreview',
+ Object.assign(Object.assign({}, item), this.getDetail(index))
+ );
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '../../libs/css/components.scss';
+ $u-upload-preview-border-radius: 2px !default;
+ $u-upload-preview-margin: 0 8px 8px 0 !default;
+ $u-upload-image-width:80px !default;
+ $u-upload-image-height:$u-upload-image-width;
+ $u-upload-other-bgColor: rgb(242, 242, 242) !default;
+ $u-upload-other-flex:1 !default;
+ $u-upload-text-font-size:11px !default;
+ $u-upload-text-color:$u-tips-color !default;
+ $u-upload-text-margin-top:2px !default;
+ $u-upload-deletable-right:0 !default;
+ $u-upload-deletable-top:0 !default;
+ $u-upload-deletable-bgColor:rgb(55, 55, 55) !default;
+ $u-upload-deletable-height:14px !default;
+ $u-upload-deletable-width:$u-upload-deletable-height;
+ $u-upload-deletable-boder-bottom-left-radius:100px !default;
+ $u-upload-deletable-zIndex:3 !default;
+ $u-upload-success-bottom:0 !default;
+ $u-upload-success-right:0 !default;
+ $u-upload-success-border-style:solid !default;
+ $u-upload-success-border-top-color:transparent !default;
+ $u-upload-success-border-left-color:transparent !default;
+ $u-upload-success-border-bottom-color: $u-success !default;
+ $u-upload-success-border-right-color:$u-upload-success-border-bottom-color;
+ $u-upload-success-border-width:9px !default;
+ $u-upload-icon-top:0px !default;
+ $u-upload-icon-right:0px !default;
+ $u-upload-icon-h5-top:1px !default;
+ $u-upload-icon-h5-right:0 !default;
+ $u-upload-icon-width:16px !default;
+ $u-upload-icon-height:$u-upload-icon-width;
+ $u-upload-success-icon-bottom:-10px !default;
+ $u-upload-success-icon-right:-10px !default;
+ $u-upload-status-right:0 !default;
+ $u-upload-status-left:0 !default;
+ $u-upload-status-bottom:0 !default;
+ $u-upload-status-top:0 !default;
+ $u-upload-status-bgColor:rgba(0, 0, 0, 0.5) !default;
+ $u-upload-status-icon-Zindex:1 !default;
+ $u-upload-message-font-size:12px !default;
+ $u-upload-message-color:#FFFFFF !default;
+ $u-upload-message-margin-top:5px !default;
+ $u-upload-button-width:80px !default;
+ $u-upload-button-height:$u-upload-button-width;
+ $u-upload-button-bgColor:rgb(244, 245, 247) !default;
+ $u-upload-button-border-radius:2px !default;
+ $u-upload-botton-margin: 0 8px 8px 0 !default;
+ $u-upload-text-font-size:11px !default;
+ $u-upload-text-color:$u-tips-color !default;
+ $u-upload-text-margin-top: 2px !default;
+ $u-upload-hover-bgColor:rgb(230, 231, 233) !default;
+ $u-upload-disabled-opacity:.5 !default;
+
+ .u-upload {
+ @include flex(column);
+ flex: 1;
+
+ &__wrap {
+ @include flex;
+ flex-wrap: wrap;
+ flex: 1;
+
+ &__preview {
+ border-radius: $u-upload-preview-border-radius;
+ margin: $u-upload-preview-margin;
+ position: relative;
+ overflow: hidden;
+ @include flex;
+
+ &__image {
+ width: $u-upload-image-width;
+ height: $u-upload-image-height;
+ }
+
+ &__other {
+ width: $u-upload-image-width;
+ height: $u-upload-image-height;
+ background-color: $u-upload-other-bgColor;
+ flex: $u-upload-other-flex;
+ @include flex(column);
+ justify-content: center;
+ align-items: center;
+
+ &__text {
+ font-size: $u-upload-text-font-size;
+ color: $u-upload-text-color;
+ margin-top: $u-upload-text-margin-top;
+ }
+ }
+ }
+ }
+
+ &__deletable {
+ position: absolute;
+ top: $u-upload-deletable-top;
+ right: $u-upload-deletable-right;
+ background-color: $u-upload-deletable-bgColor;
+ height: $u-upload-deletable-height;
+ width: $u-upload-deletable-width;
+ @include flex;
+ border-bottom-left-radius: $u-upload-deletable-boder-bottom-left-radius;
+ align-items: center;
+ justify-content: center;
+ z-index: $u-upload-deletable-zIndex;
+
+ &__icon {
+ position: absolute;
+ transform: scale(0.7);
+ top: $u-upload-icon-top;
+ right: $u-upload-icon-right;
+ /* #ifdef H5 */
+ top: $u-upload-icon-h5-top;
+ right: $u-upload-icon-h5-right;
+ /* #endif */
+ }
+ }
+
+ &__success {
+ position: absolute;
+ bottom: $u-upload-success-bottom;
+ right: $u-upload-success-right;
+ @include flex;
+ // 鐢变簬weex(nvue)涓洪樋閲屽反宸寸殑KPI(閮ㄩ棬涓氱哗鑰冩牳)鐨刲aji浜х墿锛屼笉鏀寔css缁樺埗涓夎褰�
+ // 鎵�浠ュ湪nvue涓嬩娇鐢ㄥ浘鐗囷紝闈瀗vue涓嬩娇鐢╟ss瀹炵幇
+ /* #ifndef APP-NVUE */
+ border-style: $u-upload-success-border-style;
+ border-top-color: $u-upload-success-border-top-color;
+ border-left-color: $u-upload-success-border-left-color;
+ border-bottom-color: $u-upload-success-border-bottom-color;
+ border-right-color: $u-upload-success-border-right-color;
+ border-width: $u-upload-success-border-width;
+ align-items: center;
+ justify-content: center;
+ /* #endif */
+
+ &__icon {
+ /* #ifndef APP-NVUE */
+ position: absolute;
+ transform: scale(0.7);
+ bottom: $u-upload-success-icon-bottom;
+ right: $u-upload-success-icon-right;
+ /* #endif */
+ /* #ifdef APP-NVUE */
+ width: $u-upload-icon-width;
+ height: $u-upload-icon-height;
+ /* #endif */
+ }
+ }
+
+ &__status {
+ position: absolute;
+ top: $u-upload-status-top;
+ bottom: $u-upload-status-bottom;
+ left: $u-upload-status-left;
+ right: $u-upload-status-right;
+ background-color: $u-upload-status-bgColor;
+ @include flex(column);
+ align-items: center;
+ justify-content: center;
+
+ &__icon {
+ position: relative;
+ z-index: $u-upload-status-icon-Zindex;
+ }
+
+ &__message {
+ font-size: $u-upload-message-font-size;
+ color: $u-upload-message-color;
+ margin-top: $u-upload-message-margin-top;
+ }
+ }
+
+ &__button {
+ @include flex(column);
+ align-items: center;
+ justify-content: center;
+ width: $u-upload-button-width;
+ height: $u-upload-button-height;
+ background-color: $u-upload-button-bgColor;
+ border-radius: $u-upload-button-border-radius;
+ margin: $u-upload-botton-margin;
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ /* #endif */
+
+ &__text {
+ font-size: $u-upload-text-font-size;
+ color: $u-upload-text-color;
+ margin-top: $u-upload-text-margin-top;
+ }
+
+ &--hover {
+ background-color: $u-upload-hover-bgColor;
+ }
+
+ &--disabled {
+ opacity: $u-upload-disabled-opacity;
+ }
+ }
+ }
+</style>
--
Gitblit v1.9.3