Commit 9c30de29 by halweg

reviews模块初版

feat : reviews 头部

feat: review布局骨架

feat :review模块0.1

feat : review布局骨架完成

feat : 遍历reviews

feat : review模块遍历留言

feat: 添加筛选框

feat : reviews toolbar and filter

feat : review js bind

feat :joshine-review js-init

feat : js-loader in reviews

feat : add review  install scritp

feat : review add images upload

feat : reviews 模块图片上传,图片地址保存

feat : reviews list cover compress

fix : reviews cover fix

refator : review list 适配小屏

fix : review list 优化

feat : review 数据填充

feat : review 手机版美化

fix:
jq validate radio 验证不通过的显示bug;
review add 按钮适配移动端;

feat : 添加pager

feat : review 模块分页完善
parent 49881120
......@@ -44,14 +44,14 @@ class InstallSchema implements InstallSchemaInterface
->newTable($installer->getTable('amasty_advanced_review_images'))
->addColumn(
'image_id',
Table::TYPE_INTEGER,
Table::TYPE_BIGINT,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Review Id'
)
->addColumn(
'review_id',
Table::TYPE_INTEGER,
Table::TYPE_BIGINT,
null,
['default' => 0, 'nullable' => false],
'Review table id'
......
<?php
namespace Joshine\Review\Block\Components;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\View\Element\Template;
class Pager extends \Magento\Theme\Block\Html\Pager
{
const DEFAULT_PAGE_SIZE = 3;
/**
* @var string
*/
protected $_template = 'Joshine_Review::Components/pager.phtml';
/**
* @var Json
*/
private $json;
public function __construct(
Template\Context $context,
Json $json,
array $data = []
) {
parent::__construct($context, $data);
$this->json = $json;
}
/**
* @return string
*/
public function getJsonData()
{
return $this->json->serialize($this->getData());
}
}
<?php
namespace Joshine\Review\Block;
use Joshine\Review\Model\ResourceModel\Images\Collection;
use Joshine\Review\Helper\ImageHelper;
use Magento\Framework\View\Element\Template;
use Joshine\Review\Model\ResourceModel\Images\CollectionFactory;
/**
* Class Images
* @package Amasty\AdvancedReview\Block
*/
class Images extends \Magento\Framework\View\Element\Template
{
//评论缩略图宽度
const REVIEW_COVER_WIDTH = 200;
/**
* @var string
*/
protected $_template = 'Joshine_Review::images.phtml';
private $reviewId;
/**
* @var int
*/
/**
* @var \Magento\Framework\Json\EncoderInterface
*/
private $jsonEncoder;
/**
* @var CollectionFactory
*/
private $collectionFactory;
/**
* @var ImageHelper
*/
private $imageHelper;
public function __construct(
Template\Context $context,
CollectionFactory $collectionFactory,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
ImageHelper $imageHelper,
array $data = []
) {
parent::__construct($context, $data);
$this->jsonEncoder = $jsonEncoder;
$this->collectionFactory = $collectionFactory;
$this->imageHelper = $imageHelper;
}
/**
* @return int
*/
public function getReviewId(): int
{
return $this->reviewId;
}
/**
* @param $reviewId
*
* @return $this
*/
public function setReviewId($reviewId): Images
{
$this->reviewId = $reviewId;
return $this;
}
public function getCollection() {
/** @var Collection $collection */
$collection = $this->collectionFactory->create()
->addFieldToSelect('*')
->addFieldToFilter('review_id', $this->getReviewId());
return $collection;
}
public function getFullImagePath($item): string
{
return $this->imageHelper->getFullPath($item->getPath());
}
/**
* @param $item
* @return string
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getResizedImagePath($item): string
{
return $this->imageHelper->resize($item->getPath(), self::REVIEW_COVER_WIDTH * 2);
}
}
<?php
namespace Joshine\Review\Block;
class JsInit extends \Magento\Framework\View\Element\Template {
protected $_template = 'Joshine_Review::review-js.phtml';
}
\ No newline at end of file
<?php
/**
* @author Joshine Team
* @copyright Copyright (c) 2021 Amasty (https://www.joshine.com)
* @package Joshine_AdvancedReview
*/
namespace Joshine\Review\Block\Review\Product\View;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\DataObjectFactory;
use Magento\Review\Model\ResourceModel\Review\Collection as ReviewCollection;
namespace Joshine\Review\Block\Review\Product\View;
use Magento\Review\Model\Review\SummaryFactory;
use Joshine\Review\Block\Components\Pager;
class ListView extends \Magento\Review\Block\Product\View\ListView
{
/**
* Prepare product review list toolbar
*
* @return \Amasty\AdvancedReview\Block\Review\Product\View\ListView
* @throws \Magento\Framework\Exception\LocalizedException
* @var \Magento\Review\Model\Review\SummaryFactory
*/
private $summaryModFactory;
private $dataObjectFactory;
/**
* @var ReviewCollection
*/
private $reviewsCollection;
private $reviewsColFactory;
public function __construct(
DataObjectFactory $dataObjectFactory,
SummaryFactory $summaryModFactory,
\Magento\Review\Model\ResourceModel\Review\CollectionFactory $reviewsColFactory,
\Magento\Catalog\Block\Product\Context $context, \Magento\Framework\Url\EncoderInterface $urlEncoder, \Magento\Framework\Json\EncoderInterface $jsonEncoder, \Magento\Framework\Stdlib\StringUtils $string, \Magento\Catalog\Helper\Product $productHelper, \Magento\Catalog\Model\ProductTypes\ConfigInterface $productTypeConfig, \Magento\Framework\Locale\FormatInterface $localeFormat, \Magento\Customer\Model\Session $customerSession, ProductRepositoryInterface $productRepository, \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, \Magento\Review\Model\ResourceModel\Review\CollectionFactory $collectionFactory, array $data = [])
{
$this->summaryModFactory = $summaryModFactory;
$this->dataObjectFactory = $dataObjectFactory;
$this->reviewsColFactory = $reviewsColFactory;
parent::__construct($context, $urlEncoder, $jsonEncoder, $string, $productHelper, $productTypeConfig, $localeFormat, $customerSession, $productRepository, $priceCurrency, $collectionFactory, $data);
}
protected function _prepareLayout()
{
$toolbar = $this->getLayout()->getBlock('product_review_list.toolbar');
if ($toolbar) {
$toolbar->setCollection($this->getReviewsCollection());
parent::_prepareLayout();
if ($this->getReviewsCollection()) {
$pager = $this->getLayout()->createBlock(
Pager::class
)->setCollection(
$this->getLimitedReviewsCollection()
)->setData($this->getData())
->setProductId($this->getProductId());
$this->setChild('pager', $pager);
$this->getLimitedReviewsCollection()->load();
}
return $this;
}
public function setSummary($product, $displayedCollection)
{
$storeId = $this->_storeManager->getStore()->getId();
$summaryData = $this->summaryModFactory->create()
->setStoreId($storeId)
->load($product->getId());
$summary = $this->dataObjectFactory->create();
$summary->setData($summaryData->getData());
$product->setRatingSummary($summary);
}
public function getRatingSummary()
{
$result = null;
$summary = $this->getProduct()->getRatingSummary();
if ($summary) {
$result = $summary->getRatingSummary();
}
return $result;
}
function getRatingSummaryValue(): float
{
$value = $this->getRatingSummary();
$value = $value / 100 * 5;
return round($value, 1);
}
public function getDetailedSummary(): array
{
$result = [
'3' => 0,
'2' => 0,
'1' => 0
];
foreach ($this->getReviewsCollection() as $item) {
if($item->getSizeFits() != 0 ){
$key = $item->getSizeFits();
$result[$key] = $result[$key] + 1;
}
}
return $result;
}
/**
* Get collection of reviews
*
* @return ReviewCollection
*/
public function getReviewsCollection()
{
if (null === $this->reviewsCollection) {
$this->reviewsCollection = $this->reviewsColFactory->create()->addStoreFilter(
$this->_storeManager->getStore()->getId()
)->addStatusFilter(
\Magento\Review\Model\Review::STATUS_APPROVED
)->addEntityFilter(
'product',
$this->getProduct()->getId()
)->setDateOrder();
}
return $this->reviewsCollection;
}
public function getReviewsCount() : int
{
return $this->getReviewsCollection()->getSize();
}
public function getLimitedReviewsCollection(): ReviewCollection
{
$page = $this->getRequest()->getParam('p') ?? 1;
return $this->getReviewsCollection()->clear()
->setPageSize(Pager::DEFAULT_PAGE_SIZE)
->setCurPage((int)$page)
->load()->addRateVotes();
}
public function getPagerHtml()
{
return $this->getChildHtml('pager');
}
}
<?php
namespace Joshine\Review\Block\Review;
use Amasty\AdvancedReview\Model\Toolbar\Applier;
use Amasty\AdvancedReview\Model\Toolbar\UrlBuilder;
use Magento\Framework\View\Element\Template;
class ToolBar extends Template {
protected $request;
protected $_template = 'Joshine_Review::toolbar.phtml';
public function __construct(
Template\Context $context,
\Magento\Framework\App\Request\Http $request,
array $data = []
) {
parent::__construct($context, $data);
$this->request = $request;
}
}
<?php
namespace Joshine\Review\Contract;
interface ImagesInterface
{
const IMAGE_ID = 'image_id';
const REVIEW_ID = 'review_id';
const PATH = 'path';
/**
* Returns image id field
*
* @return int|null
*/
public function getImageId();
/**
* @param int $imageId
*
* @return $this
*/
public function setImageId($imageId);
/**
* Returns review id field
*
* @return int|null
*/
public function getReviewId();
/**
* @param int $reviewId
*
* @return $this
*/
public function setReviewId($reviewId);
/**
* Returns image path
*
* @return string|null
*/
public function getPath();
/**
* @param string $path
*
* @return $this
*/
public function setPath($path);
}
<?php
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
namespace Joshine\Review\Helper;
namespace Amasty\AdvancedReview\Helper;
use Amasty\AdvancedReview\Block\Comment\Form as CommentForm;
use Amasty\AdvancedReview\Block\Images;
use Amasty\AdvancedReview\Block\Review\Toolbar;
use Amasty\AdvancedReview\Block\Helpful;
use Amasty\AdvancedReview\Block\Summary;
use Amasty\AdvancedReview\Block\Sizefits;
use Amasty\AdvancedReview\Model\Sources\Recommend;
use Amasty\AdvancedReview\Model\Sources\UseDefaultConfig;
use Magento\Catalog\Model\Product;
use Magento\Review\Model\ResourceModel\Review\Collection as ReviewCollection;
use Joshine\Review\Block\Images;
use Joshine\Review\Block\Review\ToolBar;
class BlockHelper implements \Magento\Framework\Data\CollectionDataSourceInterface
{
const ADMIN_ANSWER_ACCOUNT_ONLY = 'amasty_admin_answer_account_only';
/**
* @var \Magento\Framework\View\Element\BlockFactory
*/
private $blockFactory;
/**
* @var Config
*/
private $config;
/**
* @var \Magento\Framework\Stdlib\StringUtils
......@@ -59,28 +38,37 @@ class BlockHelper implements \Magento\Framework\Data\CollectionDataSourceInterfa
*/
private $request;
/**
* @var \Amasty\AdvancedReview\Model\Sources\Sort
*/
private $sortModel;
public function __construct(
\Magento\Framework\View\Element\BlockFactory $blockFactory,
\Amasty\AdvancedReview\Helper\Config $config,
\Magento\Framework\Stdlib\StringUtils $stringUtils,
\Magento\Customer\Model\SessionFactory $sessionFactory,
\Magento\Framework\Escaper $escaper,
\Magento\Framework\UrlInterface $urlBuilder,
\Magento\Framework\App\RequestInterface $request,
\Amasty\AdvancedReview\Model\Sources\Sort $sortModel
\Magento\Framework\App\RequestInterface $request
) {
$this->blockFactory = $blockFactory;
$this->config = $config;
$this->stringUtils = $stringUtils;
$this->sessionFactory = $sessionFactory;
$this->escaper = $escaper;
$this->urlBuilder = $urlBuilder;
$this->request = $request;
$this->sortModel = $sortModel;
}
public function getReviewImagesHtml($reviewId, $addSizeStyles = true)
{
$html = '';
$block = $this->blockFactory
->createBlock(Images::class)
->setReviewId($reviewId);
if ($block) {
$html = $block->toHtml();
}
return $html;
}
public function getToolBarHtML()
{
return $this->blockFactory
->createBlock(ToolBar::class)
->toHtml();
}
}
<?php
namespace Joshine\Review\Helper;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\UrlInterface;
class ImageHelper
{
const IMAGE_PATH = '/joshine/review/';
const IMAGE_TMP_PATH = '/joshine/review/tmp/';
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
private $storeManager;
/**
* @var \Magento\Framework\Filesystem
*/
private $filesystem;
/**
* @var \Magento\Framework\Image\AdapterFactory
*/
private $imageFactory;
/**
* @var \Magento\Framework\Filesystem\Io\File
*/
private $fileManager;
public function __construct(
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\Filesystem $filesystem,
\Magento\Framework\Image\AdapterFactory $imageFactory,
\Magento\Framework\Filesystem\Io\File $fileManager
) {
$this->storeManager = $storeManager;
$this->filesystem = $filesystem;
$this->imageFactory = $imageFactory;
$this->fileManager = $fileManager;
}
/**
* @param $name
* @return string
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getFullPath($name)
{
$path = $this->storeManager->getStore()->getBaseUrl(UrlInterface::URL_TYPE_MEDIA);
$path = trim($path, '/');
$name = trim($name, '/');
$path .= '/joshine/review/';
return $path . $name;
}
/**
* @param $image
* @param int $width
* @param int $height
* @return string
* @throws \Exception
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function resize($image, $width = null, $height = null)
{
$mediaUrl = $this->storeManager->getStore()->getBaseUrl(
UrlInterface::URL_TYPE_MEDIA
);
$mediaUrl = trim($mediaUrl, '/');
$resizedURL = $mediaUrl . self::IMAGE_PATH . 'resized/' . $width . '/' . $image;
$absolutePath = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)
->getAbsolutePath(self::IMAGE_PATH)
. $image;
$imageResized = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)
->getAbsolutePath(self::IMAGE_PATH . 'resized/' . $width . '/')
. $image;
if ($this->fileManager->fileExists($imageResized)) {
return $resizedURL;
}
if (!$this->fileManager->fileExists($absolutePath)) {
return '';
}
//create image factory...
$imageResize = $this->imageFactory->create();
$imageResize->open($absolutePath);
$imageResize->constrainOnly(true);
$imageResize->keepTransparency(true);
$imageResize->keepFrame(false);
$imageResize->backgroundColor([255, 255, 255]);
$imageResize->keepAspectRatio(true);
$imageResize->resize($width, $height);
$destination = $imageResized;
$imageResize->save($destination);
return $resizedURL;
}
}
<?php
namespace Joshine\Review\Model;
use Joshine\Review\Helper\ImageHelper;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\MediaStorage\Model\File\Uploader;
class ImageUploader
{
/**
* @var \Magento\Framework\Filesystem
*/
private $filesystem;
/**
* @var \Magento\MediaStorage\Model\File\UploaderFactory
*/
private $fileUploaderFactory;
/**
* @var \Magento\Framework\Image\AdapterFactory
*/
private $adapterFactory;
/**
* @var \Magento\Framework\Filesystem\Io\File
*/
private $ioFile;
public function __construct(
\Magento\Framework\Filesystem $filesystem,
\Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory,
\Magento\Framework\Image\AdapterFactory $adapterFactory,
\Magento\Framework\Filesystem\Io\File $ioFile
) {
$this->filesystem = $filesystem;
$this->fileUploaderFactory = $fileUploaderFactory;
$this->adapterFactory = $adapterFactory;
$this->ioFile = $ioFile;
}
public function execute(array $file, $isTmp = false)
{
$path = $this->filesystem->getDirectoryRead(
DirectoryList::MEDIA
)->getAbsolutePath(
$isTmp ? ImageHelper::IMAGE_TMP_PATH : ImageHelper::IMAGE_PATH
);
$this->ioFile->checkAndCreateFolder($path);
/** @var $uploader Uploader */
$uploader = $this->fileUploaderFactory->create(
['fileId' => $file]
);
$imageAdapter = $this->adapterFactory->create();
$uploader->addValidateCallback('catalog_product_image', $imageAdapter, 'validateUploadFile');
$uploader->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']);
$uploader->setAllowRenameFiles(true);
$uploader->setFilesDispersion(true);
$result = $uploader->save($path);
$this->trim($result);
return $result;
}
public function copy(string $imagePath)
{
$path = $this->filesystem->getDirectoryRead(
DirectoryList::MEDIA
)->getAbsolutePath(
ImageHelper::IMAGE_TMP_PATH
);
$from = $path . $imagePath;
if ($this->ioFile->fileExists($from)) {
$realPath = $this->filesystem->getDirectoryRead(
DirectoryList::MEDIA
)->getAbsolutePath(
ImageHelper::IMAGE_PATH
);
$counter = 0;
while ($this->ioFile->fileExists($realPath . $imagePath)) {
$imagePathArray = explode('.', $imagePath);
$imagePathArray[0] .= $counter++;
$imagePath = implode('.', $imagePathArray);
}
$this->ioFile->checkAndCreateFolder($this->ioFile->dirname($realPath . $imagePath));
if ($this->ioFile->mv($from, $realPath . $imagePath)) {
return $imagePath;
}
}
return false;
}
/**
* Fix for magento 2114 - setup upgrade
* @param $result
*/
private function trim($result)
{
if (isset($result['path']) && $result['file']) {
$path = rtrim($result['path'], '/') . $result['file'];
$this->ioFile->chmod($path, 0777);
}
}
}
<?php
namespace Joshine\Review\Model;
use Joshine\Review\Contract\ImagesInterface;
use Magento\Framework\Model\AbstractModel;
/**
* Class Images
* @package Amasty\AdvancedReview\Model
*/
class Images extends AbstractModel implements ImagesInterface
{
public function _construct()
{
$this->_init(\Joshine\Review\Model\ResourceModel\Images::class);
}
/**
* Returns image id field
*
* @return int|null
*/
public function getImageId()
{
return $this->getData(self::IMAGE_ID);
}
/**
* @param int $imageId
*
* @return $this
*/
public function setImageId($imageId)
{
$this->setData(self::IMAGE_ID, $imageId);
return $this;
}
/**
* Returns review id field
*
* @return int|null
*/
public function getReviewId()
{
return $this->getData(self::REVIEW_ID);
}
/**
* @param int $reviewId
*
* @return $this
*/
public function setReviewId($reviewId)
{
$this->setData(self::REVIEW_ID, $reviewId);
return $this;
}
/**
* Returns image path
*
* @return string|null
*/
public function getPath()
{
return $this->getData(self::PATH);
}
/**
* @param string $path
*
* @return $this
*/
public function setPath($path)
{
$this->setData(self::PATH, $path);
return $this;
}
}
<?php
namespace Joshine\Review\Model\ResourceModel;
class Images extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
const TABLE_NAME = 'joshine_review_images';
/**
* Model Initialization
*
* @return void
*/
protected function _construct()
{
$this->_init(self::TABLE_NAME, 'image_id');
}
}
<?php
namespace Joshine\Review\Model\ResourceModel\Images;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
/**
* Class Collection
* @package Amasty\AdvancedReview\Model\ResourceModel\Images
*/
class Collection extends AbstractCollection
{
public function _construct()
{
$this->_init(
\Joshine\Review\Model\Images::class,
\Joshine\Review\Model\ResourceModel\Images::class
);
}
/**
* @return array
*/
public function getImageKeys(): array
{
$this->getSelect()->columns('CONCAT(review_id,path) as image_key');
return $this->getColumnValues('image_key');
}
}
<?php
namespace Joshine\Review\Plugin\Review\Block;
use Magento\Review\Block\Form as MagentoForm;
class Form
{
/**
* @var \Joshine\Review\Helper\BlockHelper
*/
private $blockHelper;
public function __construct(
\Joshine\Review\Helper\BlockHelper $blockHelper
) {
$this->blockHelper = $blockHelper;
}
public function afterToHtml(
MagentoForm $subject,
$result
) {
$search = '</fieldset>';
$replace = $this->getImageUploadHtml() . $search;
$result = substr_replace($result, $replace, strrpos($result, $search), strlen($search));
$searchForm = 'data-role="product-review-form"';
return str_replace($searchForm, $searchForm . ' enctype="multipart/form-data" ', $result);
}
protected function getImageUploadHtml(): string
{
return sprintf(
'<div class="field review-field-image %s">
<label class="label">%s</label><div class="control">
<input class="joshine-input" name="review_images[]" accept="image/*" multiple %s type="file" title="%s">
</div></div>',
'',
__('Add your photo'),
'',
__('Add your photo')
);
}
}
\ No newline at end of file
<?php
namespace Joshine\Review\Plugin\Review\Model;
use Joshine\Review\Model\ImageUploader;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Review\Block\Form as MagentoForm;
use Magento\Framework\Exception\LocalizedException;
use Magento\Review\Model\Review as MagentoReview;
class Review {
/**
* @var \Magento\Framework\App\RequestInterface
*/
private $request;
/**
* @var ImageUploader
*/
private $imageUploader;
/**
* @var \Joshine\Review\Model\ImagesFactory
*/
private $imagesFactory;
private $imagesResource;
private $messageManager;
public function __construct(
\Magento\Framework\App\RequestInterface $request,
ImageUploader $imageUploader,
\Joshine\Review\Model\ImagesFactory $imagesFactory,
\Magento\Framework\Message\ManagerInterface $messageManager,
\Joshine\Review\Model\ResourceModel\Images $imagesResource
)
{
$this->messageManager = $messageManager;
$this->imagesFactory = $imagesFactory;
$this->imageUploader = $imageUploader;
$this->request = $request;
$this->imagesResource = $imagesResource;
}
public function afterAggregate(
MagentoReview $subject,
$result
) {
$this->uploadReviewImages($subject);
return $result;
}
public function uploadReviewImages(MagentoReview $subject) {
$files = $this->request->getFiles('review_images');
$reviewId = $subject->getReviewId();
//todo:: 用helper方法做是否允许上传图片的判断
if ($files && $reviewId) {
foreach ($files as $fileId => $file) {
if (UPLOAD_ERR_OK == $file['error']) {
$this->uploadImage($file, $reviewId);
}
}
}
}
public function uploadImage($file, $reviewId) {
try {
$result = $this->imageUploader->execute($file);
$this->saveImage($result, $reviewId);
} catch (\Exception $e) {
$this->messageManager->addErrorMessage(__('An error occurred while uploading the image.'));
throw new LocalizedException(__($e->getMessage()));
}
return $this;
}
/**
* @throws NoSuchEntityException
* @throws CouldNotSaveException
*/
private function saveImage($result, $reviewId)
{
/** @var \Joshine\Review\Model\Images $model */
$model = $this->imagesFactory->create();
$model->setReviewId($reviewId);
$model->setPath($result['file']);
if ($model->getImageId()) {
$model = $this->getFullImage($model->getImageId())->addData($model->getData());
}
try {
$this->imagesResource->save($model);
} catch (\Exception $e) {
if ($model->getImageId()) {
throw new CouldNotSaveException(
__('Unable to save image with ID %1. Error: %2', [$model->getImageId(), $e->getMessage()])
);
}
throw new CouldNotSaveException(__('Unable to save new image. Error: %1', $e->getMessage()));
}
}
private function getFullImage($imageId) {
$image = $this->imagesFactory->create();
$this->imagesResource->load($image, $imageId);
if (!$image->getImageId()) {
throw new NoSuchEntityException(__('Rule with specified ID "%1" not found.', $imageId));
}
return $image;
}
}
<?php
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
namespace Joshine\Review\Setup;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Ddl\Table;
/**
* Class InstallSchema
* @package Amasty\AdvancedReview\Setup
*/
class InstallSchema implements InstallSchemaInterface
{
/**
* @param SchemaSetupInterface $setup
* @param ModuleContextInterface $context
* @throws \Zend_Db_Exception
*/
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
/** 建新表 */
$this->createModuleTables($setup);
$this->addReviewColumns($setup);
$setup->endSetup();
}
/**
* @param SchemaSetupInterface $installer
* @throws \Zend_Db_Exception
*/
private function createModuleTables(SchemaSetupInterface $installer)
{
$table = $installer->getConnection()
->newTable($installer->getTable('joshine_review_images'))
->addColumn(
'image_id',
Table::TYPE_BIGINT,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Review Id'
)
->addColumn(
'review_id',
Table::TYPE_BIGINT,
null,
['default' => 0, 'nullable' => false],
'Review table id'
)
->addColumn(
'path',
Table::TYPE_TEXT,
'2M',
[],
'Image path'
)
->setComment('用户评价图片地址保存表');
$installer->getConnection()->createTable($table);
$tableVote = $installer->getConnection()
->newTable($installer->getTable('joshine_review_vote'))
->addColumn(
'vote_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Vote Id'
)
->addColumn(
'review_id',
Table::TYPE_INTEGER,
null,
['default' => 0, 'nullable' => false],
'Review table id'
)
->addColumn(
'type',
Table::TYPE_SMALLINT,
null,
[],
'type'
)
->addColumn(
'ip',
Table::TYPE_TEXT,
256,
[],
'ip'
)
->setComment('用户评价点赞记录表');
$installer->getConnection()->createTable($tableVote);
}
/**
* review表添加字段
* @param SchemaSetupInterface $installer
*/
private function addReviewColumns(SchemaSetupInterface $installer)
{
/**
* 添加verified_buyer字段,用于后续搜索
*/
$installer->getConnection()->addColumn(
$installer->getTable('review'),
'verified_buyer',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
'nullable' => true,
'default' => 0,
'comment' => 'Verified Buyer'
]
);
}
}
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Review\Model\Review">
<plugin name="Joshine_Review::save-images" type="Joshine\Review\Plugin\Review\Model\Review" />
</type>
</config>
<?xml version="1.0"?>
<!--
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Review\Block\Form">
<plugin name="Joshine_Review::add-file-upload-control" type="Joshine\Review\Plugin\Review\Block\Form" />
</type>
</config>
......@@ -6,5 +6,9 @@
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Joshine_Review" setup_version="0.0.3"/>
<module name="Joshine_Review" setup_version="0.0.4">
<sequence>
<module name="Magento_Review"/>
</sequence>
</module>
</config>
<?xml version="1.0"?>
<!--
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<block name="joshine.review.comments" class="Joshine\Review\Block\Comment\Container">
<block name="joshine.review.comments.messages" as="messages" class="Joshine\Review\Block\Comment\CommentsList">
<block name="joshine.review.comments.message" as="message" class="Joshine\Review\Block\Comment\Comment" />
</block>
<block name="joshine.review.comments.form" as="form" class="Amasty\AdvancedReview\Block\Comment\Form" />
</block>
</body>
</page>
<?xml version="1.0"?>
<!--
/**
* @author Amasty Team
* @copyright Copyright (c) 2021 Amasty (https://www.amasty.com)
* @package Amasty_AdvancedReview
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<block class="Magento\Framework\View\Element\Template"
template="Joshine_Review::review-js.phtml"
name="joshine.review.toolbar.js"
after="-"/>
</referenceContainer>
</body>
</page>
......@@ -10,7 +10,6 @@
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Joshine_Review::css/_button.css"/>
<css src="Joshine_Review::css/_review.css"/>
</head>
</page>
\ No newline at end of file
......@@ -4,13 +4,17 @@
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="product.info.product_additional_data" remove="true" />
<referenceBlock class="Magento\Theme\Block\Html\Pager" name="product_review_list.toolbar" remove="true"/>
<referenceContainer name="root">
<block class="Joshine\Review\Block\Review\Product\View\ListView"
name="Joshine.product.info.product_additional_data"
template="Joshine_Review::product/view/list.phtml"
before="product_review_list.toolbar">
<arguments>
<argument name="joshine-review-helper" xsi:type="object">Joshine\Review\Helper\BlockHelper</argument>
</arguments>
</block>
<block name="review.comments.js" class="Joshine\AdvancedReview\Block\Comment\JsInit" ifconfig="joshine_review/comments/enabled" />
<block name="joshine.review.js" class="Joshine\Review\Block\JsInit" ifconfig="joshine_review/comments/enabled" />
</referenceContainer>
</body>
</page>
var config = {
paths: {
"joshineReviewHandler": "Joshine_Review/js/review/handler",
},
};
\ No newline at end of file
<?php
/**
* @var $block \Joshine\Review\Block\Components\Pager
*/
?>
<?php if ($block->getCollection()->getSize() && $block->getLastPageNum() > 1): ?>
<div class="joshine-reviews-page">
<div class="joshine-pagination joshine-pagination-left" joshine-review-js="reviews-pages-<?= $block->escapeHtml($block->getProductId()); ?>" >
<?php if (!$block->isFirstPage()): ?>
<span class="joshine-pagination-btn joshine-pagination-prev"
data-href="<?= $block->escapeUrl($block->getPreviousPageUrl()); ?>"
>
<
</span>
<?php endif; ?>
<?php if ($block->canShowFirst()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getFirstPageUrl()); ?>"
>
1
</span>
<?php endif; ?>
<?php if ($block->canShowPreviousJump()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getPreviousJumpUrl()); ?>"
>
...
</span>
<?php endif; ?>
<?php foreach ($block->getFramePages() as $page): ?>
<?php if ($block->isPageCurrent($page)): ?>
<span class="joshine-pagination-btn joshine-pagination-hover active">
<?= $block->escapeHtml($page); ?>
</span>
<?php else: ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getPageUrl($page)); ?>"
>
<?= $block->escapeHtml($page) ?>
</span>
<?php endif; ?>
<?php endforeach; ?>
<?php if ($block->canShowNextJump()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getNextJumpUrl()) ?>"
>
...
</span>
<?php endif; ?>
<?php if ($block->canShowLast()): ?>
<span class="joshine-pagination-btn joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getLastPageUrl()) ?>"
>
<?= $block->escapeHtml($block->getLastPageNum()) ?>
</span>
<?php endif; ?>
<?php if (!$block->isLastPage()): ?>
<span class="joshine-pagination-btn joshine-pagination-next joshine-pagination-hover"
data-href="<?= $block->escapeUrl($block->getNextPageUrl()) ?>">
>
</span>
<?php endif; ?>
</div>
</div>
<?php endif ?>
\ No newline at end of file
<?php
/** @var \Joshine\Review\Block\Images $block */
$collection = $block->getCollection();
?>
<?php if ($collection->getSize()): ?>
<?php foreach ($collection as $item): ?>
<div class="joshine-review-pic-item joshine-col-xs-4">
<img class="review-image rounded"
src="<?= $block->getResizedImagePath($item) ?>"
/>
</div>
<?php endforeach; ?>
<?php endif;?>
<div id="popup-mpdal" >
Place Your Contents Here
</div>
\ No newline at end of file
<?php
/** @var $block */
?>
<script type="text/x-magento-init">
{
"*": {
"Joshine_Review/js/review/js-loader": { }
}
}
</script>
<div class="joshine-review-filter joshine-clearfix">
<div class="joshine-review-tab joshine-review-tab-container">
<span class="tab active joshine-inline-block joshine-font-c-deepin review-js-with-image">All Reviews</span>
<span class="tab review-with-image joshine-inline-block review-js-with-image joshine-hidden">With Image</span>
</div>
<div class="joshine-review-select-wrap joshine-hidden">
<div class="joshine-review-select-item">
<label>Rating</label>
<select data-review-js="filter" name="rating" class="review-js-filter">
<option value="1">1 Star</option>
<option value="2">2 Star</option>
<option value="3">3 Star</option>
<option value="4">4 Star</option>
<option value="5">5 Star</option>
</select>
</div>
<div class="joshine-review-select-item">
<label>Verified Buyers</label>
<input type="checkbox" name="verified_buyers" class="checkbox review-js-filter">
</div>
<div class="joshine-review-select-item"><span>Sort by</span>
<label><?= /* @noEscape */ __('Sort By') ?></label>
<select name="sorter" class="review-js-sorter">
<option value="date">Date</option>
<option value="helpfulness">Helpfulness</option>
<option value="rating">Rating</option>
</select>
</div>
</div>
</div>
.joshine-review-container {
padding: 0;
}
.joshine-review-container .joshine-review-title {
font-size: 24px;
font-weight: 1000;
text-transform: capitalize;
}
.joshine-review-container .joshine-review-averate {
display: flex;
}
.joshine-review-container .joshine-review-item {
padding: 24px;
display: -webkit-box;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
-webkit-box-pack: center;
justify-content: center;
width: 33.33%;
}
.joshine-review-container .joshine-review-container .joshine-review-averate .add-new-box {
flex-grow:0;
flex-basis:auto;
font-size: 1.2em;
padding: .2em;
}
.joshine-review-container .joshine-review-container .joshine-review-averate .name {
margin-bottom: 15px;
}
.joshine-review-container .ave-rate {
align-items: center;
display: flex;
}
.joshine-review-container .ave-rate-total {
font-size: 22px;
font-weight: 700;
padding-left: 10px;
}
.joshine-review-container .joshine-review-item .fit-item .joshine-process {
background-color: #e4e4e4;
border-radius: 4px;
width: 190px;
}
.joshine-review-container .joshine-review-item .fit-item .joshine-process .joshine-process-active{
background-color: #222;
border-radius: 4px;
}
.joshine-review-container .joshine-review-item .fit-item {
display: flex;
align-items: center;
}
.joshine-review-container .joshine-review-item .fit-item .fit-name {
width: 80px;
}
.joshine-review-container .joshine-review-item.add-new-box .add-new-button {
font-weight: 400;
font-size: 22px;
}
.joshine-review-container .joshine-review-list {
padding: 20px 0;
}
.joshine-review-container .joshine-review-list-item {
display: flex;
position: relative;
width: 100%;
border-bottom: 1px dashed #e5e5e5;
padding: 25px 0;
}
.joshine-review-container .joshine-review-list-item-left {
width: 200px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-nickname {
width: 100%;
white-space: nowrap;
margin-bottom: 15px;
}
.joshine-review-container .joshine-review-list-item-mid {
width: calc(100% - 288px);
display: -webkit-box;
display: flex;
padding-right: 150px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-detail {
padding-right: 40px;
width: 50%;
}
.joshine-review-container .joshine-review-list-item .joshine-review-pics {
width: 50%;
display: flex;
}
.joshine-review-container .joshine-review-list-item .joshine-review-pic-item {
position: relative;
flex-shrink: 0;
width: 108px;
height: 108px;
line-height: 108px;
overflow: hidden;
margin-right: 5px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-des-title {
font-weight: 400;
margin-bottom: 5px;
}
.joshine-review-container .joshine-review-list-item .joshine-review-des {
font-size: 14px;
line-height: 1.4em;
color: #5b5b5b;
}
.joshine-review-container .joshine-review-list-item .joshine-review-date {
position: absolute;
bottom: 28px;
}
.joshine-review-container .joshine-review-list-item .rate-fit {
display: inline-flex;
-webkit-box-align: center;
align-items: center;
flex-wrap: wrap;
padding-top: 10px;
}
.joshine-review-container .joshine-review-list-item .rate-fit .rate-fit-item{
margin-bottom: 5px;
margin-right: 14px;
}
.joshine-review-container .joshine-review-helpful {
display: inline-block;
position: absolute;
right: 0;
}
.joshine-review-container .joshine-review-helpful .joshine-like{
display: inline-block;
}
/*遮罩蒙层*/
.joshine-mask {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: fixed;
background: rgba(0, 0, 0, .8);
}
.joshine-review-container .joshine-review-tab-container {
margin-right: auto;
}
.joshine-review-container .joshine-review-tab {
font-size: 14px;
color: #666;
font-weight: 700;
position: relative;
display: inline-block;
}
.joshine-review-container .joshine-review-tab .tab {
padding: 25px 0;
cursor: pointer;
}
.joshine-review-container .joshine-review-tab .tab + .tab {
margin-left: 25px;
}
.joshine-review-container .joshine-review-filter {
border-bottom: 1px solid #e5e5e5;
display: flex;
align-items: center;
}
.joshine-review-container .joshine-review-select-wrap {
padding: 10px;
font-size: .9em;
}
.joshine-review-container .joshine-review-filter .joshine-review-select-item {
display: inline-block;
}
.joshine-review-container .joshine-review-filter .joshine-review-select-item + .joshine-review-select-item {
margin-left: 25px;
}
.joshine-review-container .joshine-review-select-wrap .joshine-review-select-box {
padding: 5px 0;
position: relative;
display: inline-block;
}
.joshine-review-container .joshine-review-select-wrap .joshine-review-select-box-input {
height: 34px;
line-height: 32px;
display: inline-block;
}
.joshine-review-container .joshine-review-tab .tab.active {
border-bottom: #222 solid 2px;
}
.joshine-review-container .joshine-reviews-page {
margin-top: 15px;
margin-bottom: 15px;
}
.joshine-review-container .joshine-review-select-wrap select {
width: auto;
padding: .5em 2.2em .5em .5em;
height: auto;
line-height: normal;
}
.joshine-review-pic-item img {
width: 100%;
height: 7.5em ;
object-fit: cover;
}
.joshine-review-container-mobile .joshine-review-pics {
display: flex;
flex-wrap: wrap;
}
.joshine-review-container-mobile .joshine-review-pic-item{
padding: .1em;
}
.joshine-review-container-mobile .joshine-review-item .fit-item .joshine-process {
background-color: #e4e4e4;
border-radius: 4px;
width: 190px;
}
.joshine-review-container-mobile .joshine-review-item .fit-item .joshine-process .joshine-process-active{
background-color: #222;
border-radius: 4px;
}
\ No newline at end of file
define([
'jquery',
'ko'
], function ($) {
'use strict';
$.widget('mage.joshineReviewSlider', {
_create: function () {
},
_binding : function () {
},
});
return $.mage.joshineReviewSlider
});
\ No newline at end of file
define(['jquery'], function ($) {
'use strict';
return {
_create : function () {
},
handlePage : function (element, event) {
let self = $(this);
if (element.hasClass('pagination-btn-disabled')) {
return;
}
element.siblings().removeClass('active');
element.addClass('active');
let pageLoad = {
loaderContext: $('#product-review-container'),
};
event.preventDefault();
$.ajax({
url: element.data('href'),
cache: true,
}).done(function (data) {
pageLoad.loaderContext.html(data);
});
},
_dealClass : function (element, classNames) {
for (let className of classNames) {
element.siblings().removeClass(className)
}
for (let className of classNames) {
element.addClass(className)
}
}
}
});
\ No newline at end of file
define([
'jquery',
'ko',
'joshineReviewHandler'
], function ($, ko, handler) {
'use strict';
$.widget('mage.joshineReview', {
_create: function () {
var self = this;
self._binding();
//组件初始化
self._componentLoad();
},
_componentLoad : function () {
},
_binding : function () {
$(document).on('click', '.joshine-pagination-btn', function(event){
handler.handlePage($(this), event);
});
},
});
return $.mage.joshineReview
});
define([
'jquery',
'ko',
'joshineReviewConfig'
], function ($, ko, config) {
'use strict';
return {
isApi : function (url) {
var apisArr = config.apis;
return apisArr.indexOf(url);
}
}
});
\ No newline at end of file
......@@ -13,11 +13,13 @@ define([
$(element).mage('validation', {
/** @inheritdoc */
errorPlacement: function (error, el) {
if (el.parents('#product-review-table').length) {
$('#product-review-table').siblings(this.errorElement + '.' + this.errorClass).remove();
$('#product-review-table').after(error);
} else {
} else if(el.parents('.options-list').length ) {
$('.options-list').siblings(this.errorElement + '.' + this.errorClass).remove();
$('.options-list').after(error);
} else {
el.after(error);
}
}
......
......@@ -7,7 +7,7 @@
/**
* Pager template
*
* @see \Magento\Theme\Block\Html\Pager
* @var \Magento\Theme\Block\Html\Pager $block
*/
?>
<?php if ($block->getCollection()->getSize()): ?>
......@@ -117,22 +117,6 @@
</div>
<?php endif; ?>
<?php if ($block->isShowPerPage()): ?>
<div class="limiter">
<strong class="limiter-label"><?= $block->escapeHtml(__('Show')) ?></strong>
<select id="limiter" data-mage-init='{"redirectUrl": {"event":"change"}}' class="limiter-options">
<?php foreach ($block->getAvailableLimit() as $_key => $_limit): ?>
<option value="<?= $block->escapeUrl($block->getLimitUrl($_key)) ?>"
<?php if ($block->isLimitCurrent($_key)): ?>
selected="selected"<?php endif ?>>
<?= $block->escapeHtml($_limit) ?>
</option>
<?php endforeach; ?>
</select>
<span class="limiter-text"><?= $block->escapeHtml(__('per page')) ?></span>
</div>
<?php endif ?>
<?php if ($block->getUseContainer()): ?>
</div>
<?php endif ?>
......
......@@ -11,15 +11,18 @@
.field.size-fits .field.choice {
clear: both;
display: inline-block;
padding: 0 5px;
line-height: 30px;
margin-right: 20px;
}
/* 猫头鹰选择 */
.size-fits + .size-fits {
padding: 0 5px;
}
.field.size-fits .field.choice input {
margin-right: 5px;
}
</style>
<div class="block review-add">
<div class="block review-add joshine-bg-silver joshine-hidden">
<div class="block-title"><strong><?= $block->escapeHtml(__('Write Your Own Review')) ?></strong></div>
<div class="block-content">
<?php if ($block->getAllowWriteReviewFlag()):?>
......
......@@ -1990,6 +1990,8 @@ font-weight: bold;
.product.info .review-add {
float: left;
width: 100% !important;
padding: 24px;
margin-bottom: 20px;
}
@media (max-width: 768px){
......@@ -2466,7 +2468,7 @@ tr.grand.totals {
}
}
.choice.field {
.size-fits + .size-fits {
padding-left: 15px;
}
......
......@@ -733,9 +733,9 @@
}
.joshine-center-block {
display: block;
margin-right: auto;
margin-left: auto;
display: block !important;
margin-right: auto !important;
margin-left: auto !important;
}
.joshine-pull-right {
float: right !important;
......
......@@ -13,7 +13,7 @@
/*小号评分容器*/
.joshine-rating-container.joshine-rating-small {
width: 100px;
width: 107px;
height: 18px;
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMnYxOEgweiIvPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTEuNzQzIDYuODc1TDkuNSAwIDcuMjU3IDYuODc1SDBsNS44NzEgNC4yNUwzLjYzIDE4IDkuNSAxMy43NSAxNS4zNzEgMThsLTIuMjQyLTYuODc1TDE5IDYuODc1aC03LjI1N3pNMTQuOSA4LjIwMmgtNC4xMjZMOS41IDQuMjkzbC0xLjI3NSAzLjkxSDQuMDk5bDMuMzM4IDIuNDE1LTEuMjc1IDMuOTA5TDkuNSAxMi4xMWwzLjMzOCAyLjQxNi0xLjI3NS0zLjkxIDMuMzM4LTIuNDE1eiIgZmlsbD0iI0IxQUVBRSIvPjwvc3ZnPg==);
}
......@@ -22,6 +22,26 @@
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMnYxOEgweiIvPjxwYXRoIGQ9Ik05LjUgMGwyLjI0MyA2Ljg3NUgxOWwtNS44NzEgNC4yNUwxNS4zNyAxOCA5LjUgMTMuNzUgMy42MjkgMThsMi4yNDItNi44NzVMMCA2Ljg3NWg3LjI1N0w5LjUgMHoiIGZpbGw9IiNFRDlEMDAiLz48L3N2Zz4=);
}
/*点赞图标*/
.joshine-like {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMHYxOEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiA3LjI4NkguNTU2QS41NTQuNTU0IDAgMCAwIDAgNy44NDh2OC45ODVjMCAuMzE0LjI0NS41NjEuNTU3LjU2MWgzLjM0YS41NTMuNTUzIDAgMCAwIC41NTYtLjU2MVY3Ljg0OGEuNTUzLjU1MyAwIDAgMC0uNTU3LS41NjJ6TTIwIDguMzVjMC0uOTE1LS42MjMtMS44NC0xLjgxMy0xLjg0aC01LjM5MmMuNzctMS4zOTEuOTk3LTMuMzQ3LjQ2Mi00Ljc3OEMxMi44NjMuNjggMTIuMTEuMDY2IDExLjEzOS4wMDRsLS4wMTYtLjAwMkExLjE3IDEuMTcgMCAwIDAgOS44ODkgMS4wOEM5Ljc1MSAyLjUgOS4xMzQgNS4wMSA4LjI1MSA1LjljLS43NDQuNzUtMS4zOCAxLjA2NS0yLjQzNiAxLjU4Ni0uMTUyLjA3NS0uMzIuMTU3LS40OTYuMjQ2YTEuNCAxLjQgMCAwIDEgLjAwNS4xMTd2OC44OTNsLjM3Ny4xM0M3LjQ0MyAxNy40NzggOC45NDggMTggMTEuMjQ4IDE4aDQuMzZjMS4xOSAwIDEuODEyLS45MjYgMS44MTItMS44NDFhMiAyIDAgMCAwLS4xNjMtLjc5MyAxLjcxOCAxLjcxOCAwIDAgMCAxLS41NTljLjI5Mi0uMzM0LjQ1My0uNzc5LjQ1My0xLjI1MSAwLS4yNy0uMDU1LS41NDMtLjE2My0uNzkyLjk1Ni0uMTY2IDEuNDUzLS45OTMgMS40NTMtMS44MSAwLS40NzUtLjE2OC0uOTUzLS40OTQtMS4zMDIuMzI2LS4zNS40OTQtLjgyNy40OTQtMS4zMDJ6IiBmaWxsPSIjQzRDNEM0Ii8+PC9zdmc+);
margin: 0 0 0 10px;
width: 20px;
height: 18px;
display: inline-block;
cursor: pointer;
transition: .3s;
}
.joshine-like.active {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyMHYxOEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiA3LjI4NkguNTU2QS41NTQuNTU0IDAgMCAwIDAgNy44NDh2OC45ODVjMCAuMzE0LjI0NS41NjEuNTU3LjU2MWgzLjM0YS41NTMuNTUzIDAgMCAwIC41NTYtLjU2MVY3Ljg0OGEuNTUzLjU1MyAwIDAgMC0uNTU3LS41NjJ6TTIwIDguMzVjMC0uOTE1LS42MjMtMS44NC0xLjgxMy0xLjg0aC01LjM5MmMuNzctMS4zOTEuOTk3LTMuMzQ3LjQ2Mi00Ljc3OEMxMi44NjMuNjggMTIuMTEuMDY2IDExLjEzOS4wMDRsLS4wMTYtLjAwMkExLjE3IDEuMTcgMCAwIDAgOS44ODkgMS4wOEM5Ljc1MSAyLjUgOS4xMzQgNS4wMSA4LjI1MSA1LjljLS43NDQuNzUtMS4zOCAxLjA2NS0yLjQzNiAxLjU4Ni0uMTUyLjA3NS0uMzIuMTU3LS40OTYuMjQ2YTEuNCAxLjQgMCAwIDEgLjAwNS4xMTd2OC44OTNsLjM3Ny4xM0M3LjQ0MyAxNy40NzggOC45NDggMTggMTEuMjQ4IDE4aDQuMzZjMS4xOSAwIDEuODEyLS45MjYgMS44MTItMS44NDFhMiAyIDAgMCAwLS4xNjMtLjc5MyAxLjcxOCAxLjcxOCAwIDAgMCAxLS41NTljLjI5Mi0uMzM0LjQ1My0uNzc5LjQ1My0xLjI1MSAwLS4yNy0uMDU1LS41NDMtLjE2My0uNzkyLjk1Ni0uMTY2IDEuNDUzLS45OTMgMS40NTMtMS44MSAwLS40NzUtLjE2OC0uOTUzLS40OTQtMS4zMDIuMzI2LS4zNS40OTQtLjgyNy40OTQtMS4zMDJ6IiBmaWxsPSIjMUFCOTAwIi8+PC9zdmc+);
}
.joshine-diss {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMThoMjBWMEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiAxMC43MTRILjU1NkEuNTU0LjU1NCAwIDAgMSAwIDEwLjE1MlYxLjE2N0MwIC44NTMuMjQ1LjYwNi41NTcuNjA2aDMuMzRjLjMxMSAwIC41NTYuMjQ3LjU1Ni41NjF2OC45ODVhLjU1My41NTMgMCAwIDEtLjU1Ny41NjJ6TTIwIDkuNjVjMCAuOTE1LS42MjMgMS44NC0xLjgxMyAxLjg0aC01LjM5MmMuNzcgMS4zOTEuOTk3IDMuMzQ3LjQ2MiA0Ljc3Ny0uMzk0IDEuMDUzLTEuMTQ2IDEuNjY3LTIuMTE4IDEuNzNoLS4wMTZhMS4xNyAxLjE3IDAgMCAxLTEuMjM0LTEuMDc2Yy0uMTM4LTEuNDItLjc1NS0zLjkzLTEuNjM4LTQuODItLjc0NC0uNzUtMS4zOC0xLjA2NS0yLjQzNi0xLjU4Ni0uMTUyLS4wNzUtLjMyLS4xNTctLjQ5Ni0uMjQ2YTEuNCAxLjQgMCAwIDAgLjAwNS0uMTE3VjEuMjZsLjM3Ny0uMTNDNy40NDMuNTIyIDguOTQ4IDAgMTEuMjQ4IDBoNC4zNmMxLjE5IDAgMS44MTIuOTI2IDEuODEyIDEuODQxYTIgMiAwIDAgMS0uMTYzLjc5M2MuMzk3LjA3Mi43NDMuMjY0IDEgLjU1OS4yOTIuMzM0LjQ1My43NzkuNDUzIDEuMjUxIDAgLjI3LS4wNTUuNTQzLS4xNjMuNzkyLjk1Ni4xNjYgMS40NTMuOTkzIDEuNDUzIDEuODEgMCAuNDc1LS4xNjguOTUyLS40OTQgMS4zMDIuMzI2LjM1LjQ5NC44MjcuNDk0IDEuMzAyeiIgZmlsbD0iI0M0QzRDNCIvPjwvc3ZnPg==);
}
.joshine-diss.active {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMThoMjBWMEgweiIvPjxwYXRoIGQ9Ik0zLjg5NiAxMC43MTRILjU1NkEuNTU0LjU1NCAwIDAgMSAwIDEwLjE1MlYxLjE2N0MwIC44NTMuMjQ1LjYwNi41NTcuNjA2aDMuMzRjLjMxMSAwIC41NTYuMjQ3LjU1Ni41NjF2OC45ODVhLjU1My41NTMgMCAwIDEtLjU1Ny41NjJ6TTIwIDkuNjVjMCAuOTE1LS42MjMgMS44NC0xLjgxMyAxLjg0aC01LjM5MmMuNzcgMS4zOTEuOTk3IDMuMzQ3LjQ2MiA0Ljc3Ny0uMzk0IDEuMDUzLTEuMTQ2IDEuNjY3LTIuMTE4IDEuNzNoLS4wMTZhMS4xNyAxLjE3IDAgMCAxLTEuMjM0LTEuMDc2Yy0uMTM4LTEuNDItLjc1NS0zLjkzLTEuNjM4LTQuODItLjc0NC0uNzUtMS4zOC0xLjA2NS0yLjQzNi0xLjU4Ni0uMTUyLS4wNzUtLjMyLS4xNTctLjQ5Ni0uMjQ2YTEuNCAxLjQgMCAwIDAgLjAwNS0uMTE3VjEuMjZsLjM3Ny0uMTNDNy40NDMuNTIyIDguOTQ4IDAgMTEuMjQ4IDBoNC4zNmMxLjE5IDAgMS44MTIuOTI2IDEuODEyIDEuODQxYTIgMiAwIDAgMS0uMTYzLjc5M2MuMzk3LjA3Mi43NDMuMjY0IDEgLjU1OS4yOTIuMzM0LjQ1My43NzkuNDUzIDEuMjUxIDAgLjI3LS4wNTUuNTQzLS4xNjMuNzkyLjk1Ni4xNjYgMS40NTMuOTkzIDEuNDUzIDEuODEgMCAuNDc1LS4xNjguOTUyLS40OTQgMS4zMDIuMzI2LjM1LjQ5NC44MjcuNDk0IDEuMzAyeiIgZmlsbD0iI0QyMzczNyIvPjwvc3ZnPg==);
}
/**进度条*/
.joshine-process {
width: 80px;
......@@ -41,18 +61,35 @@
.joshine-bg-default {
background-color: #f7f8fa !important;
}
.joshine-bg-gray {
.joshine-bg-darkgray {
background-color: #767676 !important;
}
.joshine-bg-gray {
background-color: #999 !important;
}
.joshine-bg-silver {
background-color: #f7f8fa;
}
.joshine-bg-img-default {
cursor: pointer;
background-image: url(../images/placeholders_small.jpg);
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
}
/*玄铁灰*/
.joshine-font-c-darkgray {
color: #767676 !important;
}
/*神秘银*/
/*沉稳灰*/
.joshine-font-c-gray {
color: #999 !important;
}
/*神秘银*/
.joshine-font-c-silver {
color: #f7f8fa;
}
/*深邃黑*/
.joshine-font-c-deepin {
color: #222222 !important;
......@@ -73,12 +110,16 @@
.joshine-font-mini-plus {
font-size:13px;
}
.joshine-font-text {
font-size:14px;
}
.joshine-foot-mark {
font-size: 13px;
line-height: 13px;
color: #999;
}
/*按钮*/
.joshine-btn {
display: inline-block;
......@@ -136,6 +177,61 @@ fieldset[disabled] a.joshine-btn {
border-color: #000;
}
.joshine-inline-block {
display: inline-block;
}
.joshine-pagination {
display: flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 24px;
user-select: none;
color: #222;
font-size: 14px;
}
.joshine-pagination .joshine-pagination-btn {
margin-left: 12px;
padding: 0 4px;
border-radius: 12px;
text-align: center;
color: #222;
font-weight: 700;
min-width: 24px;
height: 24px;
line-height: normal;
display: flex;
align-items: center;
justify-content: center;
}
.joshine-pagination .joshine-pagination-btn.active {
color: #fff;
background: #222;
}
.joshine-pagination-hover:not(.active) {
cursor: pointer;
}
.joshine-pagination-hover:not(.active):hover {
background: #e0e0e0;
}
.joshine-pagination .joshine-pagination-next {
margin-left: 12px;
}
.joshine-pagination .joshine-pagination-btn.pagination-btn-disabled {
cursor: not-allowed;
color: #c2c2c2;
}
.joshine-pagination-right {
justify-content: flex-end;
}
.joshine-pagination-left {
justify-content: flex-start;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment